[netstack] absorb upstream changes

- replace some incorrect "piecemeal" types with tcpip.ProtocolAddress
- remove lots of netstack/util wrappers which aren't useful
- fix logging deadlocks in netstack/routes
- call tcpip.Endpoint.Disconnect on AF_UNSPEC

Change-Id: I5fad493f38c540581bf7f69b24ace5f52253af7b
diff --git a/src/connectivity/network/netstack/dhcp/client.go b/src/connectivity/network/netstack/dhcp/client.go
index 0efbaac..5e67ee5 100644
--- a/src/connectivity/network/netstack/dhcp/client.go
+++ b/src/connectivity/network/netstack/dhcp/client.go
@@ -9,9 +9,9 @@
 	"context"
 	"fmt"
 	"math"
+	"net"
 	"time"
 
-	"netstack/util"
 	"syslog"
 
 	"github.com/google/netstack/rand"
@@ -350,12 +350,13 @@
 			c.server = cfg.ServerAddress
 
 			if len(cfg.SubnetMask) == 0 {
-				cfg.SubnetMask = util.DefaultMask(c.addr.Address)
+				cfg.SubnetMask = tcpip.AddressMask(net.IP(c.addr.Address).DefaultMask())
 			}
 
+			prefixLen, _ := net.IPMask(cfg.SubnetMask).Size()
 			requestedAddr = tcpip.AddressWithPrefix{
 				Address:   addr,
-				PrefixLen: util.PrefixLength(cfg.SubnetMask),
+				PrefixLen: prefixLen,
 			}
 
 			syslog.VLogTf(syslog.DebugVerbosity, tag, "got %s from %s: Address=%s, server=%s, leaseTime=%s, renewalTime=%s, rebindTime=%s", typ, srcAddr.Addr, requestedAddr, c.server, cfg.LeaseLength, cfg.RenewalTime, cfg.RebindingTime)
@@ -400,9 +401,10 @@
 			if err := cfg.decode(opts); err != nil {
 				return Config{}, fmt.Errorf("%s decode: %s", typ, err)
 			}
+			prefixLen, _ := net.IPMask(cfg.SubnetMask).Size()
 			addr := tcpip.AddressWithPrefix{
 				Address:   addr,
-				PrefixLen: util.PrefixLength(cfg.SubnetMask),
+				PrefixLen: prefixLen,
 			}
 			if addr != requestedAddr {
 				return Config{}, fmt.Errorf("%s with unexpected address=%s expected=%s", typ, addr, requestedAddr)
diff --git a/src/connectivity/network/netstack/dhcp/dhcp_test.go b/src/connectivity/network/netstack/dhcp/dhcp_test.go
index 5eae7e2..660fb18 100644
--- a/src/connectivity/network/netstack/dhcp/dhcp_test.go
+++ b/src/connectivity/network/netstack/dhcp/dhcp_test.go
@@ -6,7 +6,6 @@
 
 import (
 	"context"
-	"strings"
 	"sync/atomic"
 	"testing"
 	"time"
@@ -59,9 +58,7 @@
 	}
 
 	s.SetRouteTable([]tcpip.Route{{
-		Destination: tcpip.Address(strings.Repeat("\x00", 4)),
-		Mask:        tcpip.AddressMask(strings.Repeat("\x00", 4)),
-		Gateway:     "",
+		Destination: tcpipHeader.IPv4EmptySubnet,
 		NIC:         nicid,
 	}})
 
diff --git a/src/connectivity/network/netstack/fidlconv/fidlconv.go b/src/connectivity/network/netstack/fidlconv/fidlconv.go
index 2386c5f..5556bc0 100644
--- a/src/connectivity/network/netstack/fidlconv/fidlconv.go
+++ b/src/connectivity/network/netstack/fidlconv/fidlconv.go
@@ -5,31 +5,30 @@
 package fidlconv
 
 import (
-	"bytes"
-	"encoding/binary"
 	"fmt"
-	"math/big"
 	"net"
 
-	"netstack/util"
-
 	netfidl "fidl/fuchsia/net"
 	"fidl/fuchsia/net/stack"
 
 	"github.com/google/netstack/tcpip"
 )
 
-func ToTCPIPAddress(addr netfidl.IpAddress) tcpip.Address {
+func toNet(addr netfidl.IpAddress) net.IP {
 	switch tag := addr.Which(); tag {
 	case netfidl.IpAddressIpv4:
-		return tcpip.Address(addr.Ipv4.Addr[:])
+		return addr.Ipv4.Addr[:]
 	case netfidl.IpAddressIpv6:
-		return tcpip.Address(addr.Ipv6.Addr[:])
+		return addr.Ipv6.Addr[:]
 	default:
 		panic(fmt.Sprintf("invalid IP address tag = %d", tag))
 	}
 }
 
+func ToTCPIPAddress(addr netfidl.IpAddress) tcpip.Address {
+	return tcpip.Address(toNet(addr))
+}
+
 func ToNetIpAddress(addr tcpip.Address) netfidl.IpAddress {
 	var out netfidl.IpAddress
 	switch l := len(addr); l {
@@ -47,82 +46,43 @@
 	return out
 }
 
-func ToTCPIPSubnet(sn netfidl.Subnet) (tcpip.Subnet, error) {
-	// Use ToTCPIPAddress to abstract the IPv4 vs IPv6 behavior.
-	a := []byte(ToTCPIPAddress(sn.Addr))
-	m := util.CIDRMask(int(sn.PrefixLen), int(len(a)*8))
-	for i := range a {
-		a[i] = a[i] & m[i]
+func ToTCPIPSubnet(sn netfidl.Subnet) tcpip.Subnet {
+	a := toNet(sn.Addr)
+	m := net.CIDRMask(int(sn.PrefixLen), len(a)*8)
+	subnet, err := tcpip.NewSubnet(tcpip.Address(a.Mask(m)), tcpip.AddressMask(m))
+	if err != nil {
+		panic(err)
 	}
-	return tcpip.NewSubnet(tcpip.Address(a), m)
+	return subnet
 }
 
-func GetPrefixLen(mask tcpip.AddressMask) uint8 {
-	prefixLen := uint8(0)
-	switch len(mask) {
-	case 4:
-		var x uint32
-		if err := binary.Read(bytes.NewReader([]byte(mask)), binary.BigEndian, &x); err != nil {
-			return 0
-		}
-		if x == 0 {
-			return 0
-		}
-		for x&1 == 0 {
-			prefixLen += 1
-			x >>= 1
-		}
-		return 32 - prefixLen
-	case 16:
-		var x big.Int
-		zero := big.NewInt(0)
-		one := big.NewInt(1)
-		x.SetBytes([]byte(mask))
-
-		if x.Cmp(zero) == 0 {
-			return 0
-		}
-		var tmp big.Int
-		for tmp.And(&x, one).Cmp(zero) == 0 {
-			prefixLen += 1
-			x.Rsh(&x, 1)
-		}
-		return 128 - prefixLen
-	default:
-		panic("invalid tcpip.Address length")
+func TcpipRouteToForwardingEntry(route tcpip.Route) stack.ForwardingEntry {
+	forwardingEntry := stack.ForwardingEntry{
+		Subnet: netfidl.Subnet{
+			Addr:      ToNetIpAddress(route.Destination.ID()),
+			PrefixLen: uint8(route.Destination.Prefix()),
+		},
 	}
-}
-
-func TcpipRouteToForwardingEntry(tcpipRoute tcpip.Route) stack.ForwardingEntry {
-	var dest stack.ForwardingDestination
 	// There are two types of destinations: link-local and next-hop.
 	//   If a route has a gateway, use that as the next-hop, and ignore the NIC.
 	//   Otherwise, it is considered link-local, and use the NIC.
-	if tcpipRoute.Gateway == tcpip.Address("") {
-		dest.SetDeviceId(uint64(tcpipRoute.NIC))
+	if len(route.Gateway) == 0 {
+		forwardingEntry.Destination.SetDeviceId(uint64(route.NIC))
 	} else {
-		dest.SetNextHop(ToNetIpAddress(tcpipRoute.Gateway))
+		forwardingEntry.Destination.SetNextHop(ToNetIpAddress(route.Gateway))
 	}
-	return stack.ForwardingEntry{
-		Subnet: netfidl.Subnet{
-			Addr:      ToNetIpAddress(tcpipRoute.Destination),
-			PrefixLen: GetPrefixLen(tcpipRoute.Mask),
-		},
-		Destination: dest,
-	}
+	return forwardingEntry
 }
 
 func ForwardingEntryToTcpipRoute(forwardingEntry stack.ForwardingEntry) tcpip.Route {
-	dest := ToTCPIPAddress(forwardingEntry.Subnet.Addr)
-	tcpipRoute := tcpip.Route{
-		Destination: dest,
-		Mask:        util.CIDRMask(int(forwardingEntry.Subnet.PrefixLen), len(dest)*8),
+	route := tcpip.Route{
+		Destination: ToTCPIPSubnet(forwardingEntry.Subnet),
 	}
 	switch forwardingEntry.Destination.Which() {
 	case stack.ForwardingDestinationDeviceId:
-		tcpipRoute.NIC = tcpip.NICID(forwardingEntry.Destination.DeviceId)
+		route.NIC = tcpip.NICID(forwardingEntry.Destination.DeviceId)
 	case stack.ForwardingDestinationNextHop:
-		tcpipRoute.Gateway = ToTCPIPAddress(forwardingEntry.Destination.NextHop)
+		route.Gateway = ToTCPIPAddress(forwardingEntry.Destination.NextHop)
 	}
-	return tcpipRoute
+	return route
 }
diff --git a/src/connectivity/network/netstack/fidlconv/fidlconv_test.go b/src/connectivity/network/netstack/fidlconv/fidlconv_test.go
index 9cc4a34..fa5fb36 100644
--- a/src/connectivity/network/netstack/fidlconv/fidlconv_test.go
+++ b/src/connectivity/network/netstack/fidlconv/fidlconv_test.go
@@ -5,11 +5,12 @@
 package fidlconv
 
 import (
+	"net"
 	"testing"
 
 	"netstack/util"
 
-	"fidl/fuchsia/net"
+	fidlnet "fidl/fuchsia/net"
 	"fidl/fuchsia/net/stack"
 
 	"github.com/google/netstack/tcpip"
@@ -19,8 +20,8 @@
 // This is challenging because of the way FIDL unions are constructed in Go.
 
 func TestNetIPtoTCPIPAddressIPv4(t *testing.T) {
-	from := net.IpAddress{}
-	from.SetIpv4(net.Ipv4Address{Addr: [4]uint8{127, 0, 0, 1}})
+	from := fidlnet.IpAddress{}
+	from.SetIpv4(fidlnet.Ipv4Address{Addr: [4]uint8{127, 0, 0, 1}})
 	to := ToTCPIPAddress(from)
 	expected := util.Parse("127.0.0.1")
 	if to != expected {
@@ -29,8 +30,8 @@
 }
 
 func TestNetIPtoTCPIPAddressIPv6(t *testing.T) {
-	from := net.IpAddress{}
-	from.SetIpv6(net.Ipv6Address{Addr: [16]uint8{0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}})
+	from := fidlnet.IpAddress{}
+	from.SetIpv6(fidlnet.Ipv6Address{Addr: [16]uint8{0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}})
 	to := ToTCPIPAddress(from)
 	expected := util.Parse("fe80::1")
 	if to != expected {
@@ -41,8 +42,8 @@
 func TestToFIDLIPAddressIPv4(t *testing.T) {
 	from := util.Parse("127.0.0.1")
 	to := ToNetIpAddress(from)
-	expected := net.IpAddress{}
-	expected.SetIpv4(net.Ipv4Address{Addr: [4]uint8{127, 0, 0, 1}})
+	expected := fidlnet.IpAddress{}
+	expected.SetIpv4(fidlnet.Ipv4Address{Addr: [4]uint8{127, 0, 0, 1}})
 	if to != expected {
 		t.Fatalf("Expected:\n %v\nActual:\n %v", expected, to)
 	}
@@ -51,8 +52,8 @@
 func TestToFIDLIPAddressIPv6(t *testing.T) {
 	from := util.Parse("fe80::1")
 	to := ToNetIpAddress(from)
-	expected := net.IpAddress{}
-	expected.SetIpv6(net.Ipv6Address{Addr: [16]uint8{0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}})
+	expected := fidlnet.IpAddress{}
+	expected.SetIpv6(fidlnet.Ipv6Address{Addr: [16]uint8{0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}})
 	if to != expected {
 		t.Fatalf("Expected:\n %v\nActual:\n %v", expected, to)
 	}
@@ -96,16 +97,19 @@
 		{[4]uint8{0, 0, 0, 0}, 0, "0.0.0.0/0"},
 	}
 	for _, testCase := range cases {
-		netSubnet := newNetSubnet(testCase.addr, testCase.prefix)
-		to, err := ToTCPIPSubnet(netSubnet)
-		if err != nil {
-			t.Errorf("Error generating tcpip.Subnet: %v", err)
-			continue
+		netSubnet := fidlnet.Subnet{
+			PrefixLen: testCase.prefix,
 		}
-		_, expected, err := util.ParseCIDR(testCase.expected)
+		netSubnet.Addr.SetIpv4(fidlnet.Ipv4Address{Addr: testCase.addr})
+		to := ToTCPIPSubnet(netSubnet)
+		_, ipNet, err := net.ParseCIDR(testCase.expected)
 		if err != nil {
 			t.Fatalf("Error creating tcpip.Subnet: %v", err)
 		}
+		expected, err := tcpip.NewSubnet(tcpip.Address(ipNet.IP), tcpip.AddressMask(ipNet.Mask))
+		if err != nil {
+			t.Fatal(err)
+		}
 		if to != expected {
 			t.Errorf("Expected:\n {%v, %v}\nActual: {%v, %v}",
 				[]byte(expected.ID()), []byte(expected.Mask()),
@@ -114,70 +118,13 @@
 	}
 }
 
-func TestPrefixLenIPv4(t *testing.T) {
-	cases := []struct {
-		mask   tcpip.AddressMask
-		prefix uint8
-	}{
-		{tcpip.AddressMask(util.Parse("255.255.255.255")), 32},
-		{tcpip.AddressMask(util.Parse("255.255.255.254")), 31},
-		{tcpip.AddressMask(util.Parse("255.255.255.128")), 25},
-		{tcpip.AddressMask(util.Parse("255.255.255.0")), 24},
-		{tcpip.AddressMask(util.Parse("255.0.0.0")), 8},
-		{tcpip.AddressMask(util.Parse("128.0.0.0")), 1},
-		{tcpip.AddressMask(util.Parse("0.0.0.0")), 0},
-	}
-	for _, testCase := range cases {
-		prefixLen := GetPrefixLen(testCase.mask)
-		if prefixLen != testCase.prefix {
-			t.Errorf("Expected:\n %v\nActual: %v", testCase.prefix, prefixLen)
-		}
-	}
-}
-
-func TestPrefixLenIPv6(t *testing.T) {
-	cases := []struct {
-		mask   tcpip.AddressMask
-		prefix uint8
-	}{
-		{tcpip.AddressMask(util.Parse("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")), 128},
-		{tcpip.AddressMask(util.Parse("ffff:ffff:ffff:ffff:ffff:ffff:ffff:8000")), 113},
-		{tcpip.AddressMask(util.Parse("ffff:ffff:ffff:ffff:ffff:ffff:ffff::")), 112},
-		{tcpip.AddressMask(util.Parse("ffff:ffff:ffff:ffff:ffff:ffff:fffe::")), 111},
-		{tcpip.AddressMask(util.Parse("8000::")), 1},
-		{tcpip.AddressMask(util.Parse("::")), 0},
-	}
-	for _, testCase := range cases {
-		prefixLen := GetPrefixLen(testCase.mask)
-		if prefixLen != testCase.prefix {
-			t.Errorf("Case: %v\n Expected:\n %v\nActual: %v", testCase.mask, testCase.prefix, prefixLen)
-		}
-	}
-}
-
-func TestPrefixLenEmptyInvalid(t *testing.T) {
-	defer func() {
-		if r := recover(); r == nil {
-			t.Fatalf("Expected to fail on invalid address length")
-		}
-	}()
-
-	GetPrefixLen("")
-	t.Errorf("Expected to fail on invalid address length")
-}
-
-func TestPrefixLenInvalidLength(t *testing.T) {
-	defer func() {
-		if r := recover(); r == nil {
-			t.Fatalf("Expected to fail on invalid address length")
-		}
-	}()
-
-	GetPrefixLen("\x00\x00")
-	t.Errorf("Expected to fail on invalid address length")
-}
-
 func TestForwardingEntryAndTcpipRouteConversions(t *testing.T) {
+	const gateway = "efghijklmnopqrst"
+
+	destination, err := tcpip.NewSubnet("\xab\xcd\x00\x00", "\xff\xff\xe0\x00")
+	if err != nil {
+		t.Fatal(err)
+	}
 	for _, tc := range []struct {
 		dest stack.ForwardingDestination
 		want tcpip.Route
@@ -188,24 +135,22 @@
 			return dest
 		}(),
 			want: tcpip.Route{
-				Destination: tcpip.Address("abcd"),
-				Mask:        "\xff\xff\xe0\x00",
+				Destination: destination,
 				NIC:         789,
 			}},
 		{dest: func() stack.ForwardingDestination {
 			var dest stack.ForwardingDestination
-			dest.SetNextHop(ToNetIpAddress(tcpip.Address("efghijklmnopqrst")))
+			dest.SetNextHop(ToNetIpAddress(gateway))
 			return dest
 		}(),
 			want: tcpip.Route{
-				Destination: tcpip.Address("abcd"),
-				Mask:        "\xff\xff\xe0\x00",
-				Gateway:     tcpip.Address("efghijklmnopqrst"),
+				Destination: destination,
+				Gateway:     gateway,
 			}},
 	} {
 		fe := stack.ForwardingEntry{
-			Subnet: net.Subnet{
-				Addr:      ToNetIpAddress(tcpip.Address("abcd")),
+			Subnet: fidlnet.Subnet{
+				Addr:      ToNetIpAddress(destination.ID()),
 				PrefixLen: 19,
 			},
 			Destination: tc.dest,
@@ -220,10 +165,3 @@
 		}
 	}
 }
-
-func newNetSubnet(addr [4]uint8, prefix uint8) net.Subnet {
-	subnet := net.Subnet{}
-	subnet.Addr.SetIpv4(net.Ipv4Address{Addr: addr})
-	subnet.PrefixLen = prefix
-	return subnet
-}
diff --git a/src/connectivity/network/netstack/filter/fidlconv.go b/src/connectivity/network/netstack/filter/fidlconv.go
index 8805d12..1cb08df 100644
--- a/src/connectivity/network/netstack/filter/fidlconv.go
+++ b/src/connectivity/network/netstack/filter/fidlconv.go
@@ -5,10 +5,9 @@
 package filter
 
 import (
-	"netstack/util"
-
 	"fidl/fuchsia/net"
 	"fidl/fuchsia/net/filter"
+	"netstack/fidlconv"
 
 	"github.com/google/netstack/tcpip"
 	"github.com/google/netstack/tcpip/header"
@@ -137,16 +136,6 @@
 	}, nil
 }
 
-func toSubnet(o *net.Subnet) (tcpip.Subnet, error) {
-	addr, err := toAddress(&o.Addr)
-	if err != nil {
-		return tcpip.Subnet{}, ErrBadAddress
-	}
-	mask := util.CIDRMask(int(o.PrefixLen), 8*len(addr))
-	subnet, err := tcpip.NewSubnet(addr, mask)
-	return subnet, err
-}
-
 func fromRule(o *Rule) (filter.Rule, error) {
 	action, err := fromAction(o.action)
 	if err != nil {
@@ -207,17 +196,11 @@
 	}
 	var srcSubnet, dstSubnet *tcpip.Subnet
 	if o.SrcSubnet != nil {
-		subnet, err := toSubnet(o.SrcSubnet)
-		if err != nil {
-			return Rule{}, err
-		}
+		subnet := fidlconv.ToTCPIPSubnet(*o.SrcSubnet)
 		srcSubnet = &subnet
 	}
 	if o.DstSubnet != nil {
-		subnet, err := toSubnet(o.DstSubnet)
-		if err != nil {
-			return Rule{}, err
-		}
+		subnet := fidlconv.ToTCPIPSubnet(*o.DstSubnet)
 		dstSubnet = &subnet
 	}
 	return Rule{
@@ -287,14 +270,11 @@
 	if err != nil {
 		return NAT{}, err
 	}
-	srcSubnet, err := toSubnet(&o.SrcSubnet)
-	if err != nil {
-		return NAT{}, err
-	}
 	newSrcAddr, err := toAddress(&o.NewSrcAddr)
 	if err != nil {
 		return NAT{}, err
 	}
+	srcSubnet := fidlconv.ToTCPIPSubnet(o.SrcSubnet)
 	return NAT{
 		transProto: transProto,
 		srcSubnet:  &srcSubnet,
diff --git a/src/connectivity/network/netstack/filter/fidlconv_test.go b/src/connectivity/network/netstack/filter/fidlconv_test.go
index b96fe6f..fa9a83b 100644
--- a/src/connectivity/network/netstack/filter/fidlconv_test.go
+++ b/src/connectivity/network/netstack/filter/fidlconv_test.go
@@ -5,12 +5,13 @@
 package filter
 
 import (
+	"net"
 	"reflect"
 	"testing"
 
 	"netstack/util"
 
-	"fidl/fuchsia/net"
+	fidlnet "fidl/fuchsia/net"
 	"fidl/fuchsia/net/filter"
 
 	"github.com/google/netstack/tcpip"
@@ -177,25 +178,25 @@
 
 func TestFromAddress(t *testing.T) {
 	a1 := util.Parse("1.2.3.4")
-	var b1 net.IpAddress
+	var b1 fidlnet.IpAddress
 
-	b1.SetIpv4(net.Ipv4Address{Addr: [4]uint8{1, 2, 3, 4}})
+	b1.SetIpv4(fidlnet.Ipv4Address{Addr: [4]uint8{1, 2, 3, 4}})
 
 	a2 := util.Parse("a:b:c::2:3:4")
-	var b2v6 net.Ipv6Address
+	var b2v6 fidlnet.Ipv6Address
 	copy(b2v6.Addr[:], "\x00\x0a\x00\x0b\x00\x0c\x00\x00\x00\x00\x00\x02\x00\x03\x00\x04")
-	var b2 net.IpAddress
+	var b2 fidlnet.IpAddress
 
 	b2.SetIpv6(b2v6)
 
 	var tests = []struct {
 		address    tcpip.Address
-		netAddress net.IpAddress
+		netAddress fidlnet.IpAddress
 		err        error
 	}{
 		{a1, b1, nil},
 		{a2, b2, nil},
-		{tcpip.Address("12345"), net.IpAddress{}, ErrUnknownAddressType},
+		{tcpip.Address("12345"), fidlnet.IpAddress{}, ErrUnknownAddressType},
 	}
 
 	for _, test := range tests {
@@ -211,11 +212,11 @@
 			t.Errorf("fromAddress got.Which()=%v, want.Which()=%v", got.Which(), want.Which())
 		}
 		switch got.Which() {
-		case net.IpAddressIpv4:
+		case fidlnet.IpAddressIpv4:
 			if got.Ipv4 != want.Ipv4 {
 				t.Errorf("fromAddress got.Ipv4=%v, want.Ipv4=%v", got.Ipv4, want.Ipv4)
 			}
-		case net.IpAddressIpv6:
+		case fidlnet.IpAddressIpv6:
 			if got.Ipv6 != want.Ipv6 {
 				t.Errorf("fromAddress got.Ipv6=%v, want.Ipv6=%v", got.Ipv6, want.Ipv6)
 			}
@@ -226,26 +227,26 @@
 }
 
 func TestToAddress(t *testing.T) {
-	var a1 net.IpAddress
-	a1.SetIpv4(net.Ipv4Address{Addr: [4]uint8{1, 2, 3, 4}})
+	var a1 fidlnet.IpAddress
+	a1.SetIpv4(fidlnet.Ipv4Address{Addr: [4]uint8{1, 2, 3, 4}})
 
 	b1 := util.Parse("1.2.3.4")
 
-	var a2v6 net.Ipv6Address
+	var a2v6 fidlnet.Ipv6Address
 	copy(a2v6.Addr[:], "\x00\x0a\x00\x0b\x00\x0c\x00\x00\x00\x00\x00\x02\x00\x03\x00\x04")
-	var a2 net.IpAddress
+	var a2 fidlnet.IpAddress
 	a2.SetIpv6(a2v6)
 
 	b2 := util.Parse("a:b:c::2:3:4")
 
 	var tests = []struct {
-		netAddress net.IpAddress
+		netAddress fidlnet.IpAddress
 		address    tcpip.Address
 		err        error
 	}{
 		{a1, b1, nil},
 		{a2, b2, nil},
-		{net.IpAddress{}, tcpip.Address(""), ErrUnknownAddressType},
+		{fidlnet.IpAddress{}, tcpip.Address(""), ErrUnknownAddressType},
 	}
 
 	for _, test := range tests {
@@ -262,42 +263,50 @@
 	}
 }
 
+func parseCIDR(s string) (tcpip.Subnet, error) {
+	_, subnet, err := net.ParseCIDR(s)
+	if err != nil {
+		return tcpip.Subnet{}, err
+	}
+	return tcpip.NewSubnet(tcpip.Address(subnet.IP), tcpip.AddressMask(subnet.Mask))
+}
+
 func TestFromSubnet(t *testing.T) {
 	// test1
-	_, s1, err := util.ParseCIDR("1.2.3.4/32")
+	s1, err := parseCIDR("1.2.3.4/32")
 	if err != nil {
 		t.Errorf("ParseCIDR error: %v", err)
 	}
 
-	var a1 net.IpAddress
-	a1.SetIpv4(net.Ipv4Address{Addr: [4]uint8{1, 2, 3, 4}})
-	t1 := net.Subnet{Addr: a1, PrefixLen: 32}
+	var a1 fidlnet.IpAddress
+	a1.SetIpv4(fidlnet.Ipv4Address{Addr: [4]uint8{1, 2, 3, 4}})
+	t1 := fidlnet.Subnet{Addr: a1, PrefixLen: 32}
 
 	// test2
-	_, s2, err := util.ParseCIDR("10.0.0.0/8")
+	s2, err := parseCIDR("10.0.0.0/8")
 	if err != nil {
 		t.Errorf("ParseCIDR error: %v", err)
 	}
 
-	var a2 net.IpAddress
-	a2.SetIpv4(net.Ipv4Address{Addr: [4]uint8{10, 0, 0, 0}})
-	t2 := net.Subnet{Addr: a2, PrefixLen: 8}
+	var a2 fidlnet.IpAddress
+	a2.SetIpv4(fidlnet.Ipv4Address{Addr: [4]uint8{10, 0, 0, 0}})
+	t2 := fidlnet.Subnet{Addr: a2, PrefixLen: 8}
 
 	// test3
-	_, s3, err := util.ParseCIDR("a:b:c::/96")
+	s3, err := parseCIDR("a:b:c::/96")
 	if err != nil {
 		t.Errorf("ParseCIDR error: %v", err)
 	}
 
-	var a3v6 net.Ipv6Address
+	var a3v6 fidlnet.Ipv6Address
 	copy(a3v6.Addr[:], "\x00\x0a\x00\x0b\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
-	var a3 net.IpAddress
+	var a3 fidlnet.IpAddress
 	a3.SetIpv6(a3v6)
-	t3 := net.Subnet{Addr: a3, PrefixLen: 96}
+	t3 := fidlnet.Subnet{Addr: a3, PrefixLen: 96}
 
 	var tests = []struct {
 		subnet    tcpip.Subnet
-		netSubnet net.Subnet
+		netSubnet fidlnet.Subnet
 		err       error
 	}{
 		{s1, t1, nil},
@@ -318,11 +327,11 @@
 			t.Errorf("fromSubnet got.Addr.Which()=%v, want.Addr.Which()=%v", got.Addr.Which(), want.Addr.Which())
 		}
 		switch got.Addr.Which() {
-		case net.IpAddressIpv4:
+		case fidlnet.IpAddressIpv4:
 			if got.Addr.Ipv4 != want.Addr.Ipv4 {
 				t.Errorf("fromSubnet got.Addr.Ipv4=%v, want.Addr.Ipv4=%v", got.Addr.Ipv4, want.Addr.Ipv4)
 			}
-		case net.IpAddressIpv6:
+		case fidlnet.IpAddressIpv6:
 			if got.Addr.Ipv6 != want.Addr.Ipv6 {
 				t.Errorf("fromSubnet got.Addr.Ipv6=%v, want.Addr.Ipv6=%v", got.Addr.Ipv6, want.Addr.Ipv6)
 			}
@@ -335,73 +344,12 @@
 	}
 }
 
-func TestToSubnet(t *testing.T) {
-	// test1
-	var a1 net.IpAddress
-	a1.SetIpv4(net.Ipv4Address{Addr: [4]uint8{1, 2, 3, 4}})
-	s1 := net.Subnet{Addr: a1, PrefixLen: 32}
-
-	_, t1, err := util.ParseCIDR("1.2.3.4/32")
-	if err != nil {
-		t.Errorf("ParseCIDR error: %v", err)
-	}
-
-	// test2
-	var a2 net.IpAddress
-	a2.SetIpv4(net.Ipv4Address{Addr: [4]uint8{10, 0, 0, 0}})
-	s2 := net.Subnet{Addr: a2, PrefixLen: 8}
-
-	_, t2, err := util.ParseCIDR("10.0.0.0/8")
-	if err != nil {
-		t.Errorf("ParseCIDR error: %v", err)
-	}
-
-	// test3
-	var a3v6 net.Ipv6Address
-	copy(a3v6.Addr[:], "\x00\x0a\x00\x0b\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
-	var a3 net.IpAddress
-	a3.SetIpv6(a3v6)
-	s3 := net.Subnet{Addr: a3, PrefixLen: 96}
-
-	_, t3, err := util.ParseCIDR("a:b:c::/96")
-	if err != nil {
-		t.Errorf("ParseCIDR error: %v", err)
-	}
-
-	var tests = []struct {
-		netSubnet net.Subnet
-		subnet    tcpip.Subnet
-		err       error
-	}{
-		{s1, t1, nil},
-		{s2, t2, nil},
-		{s3, t3, nil},
-	}
-
-	for _, test := range tests {
-		subnet, err := toSubnet(&test.netSubnet)
-		if err != test.err {
-			t.Errorf("toSubnet unxpected error: %v", err)
-		}
-		if err != nil {
-			continue
-		}
-		got, want := subnet, test.subnet
-		if got.ID() != want.ID() {
-			t.Errorf("toSubnet got.ID()=%v, want.ID()=%v", got.ID(), want.ID())
-		}
-		if got.Mask() != want.Mask() {
-			t.Errorf("toSubnet got.Mask()=%x, want.Mask()=%x", got.Mask(), want.Mask())
-		}
-	}
-}
-
 func TestRules(t *testing.T) {
-	_, srcSubnet, err := util.ParseCIDR("1.2.3.4/32")
+	srcSubnet, err := parseCIDR("1.2.3.4/32")
 	if err != nil {
 		t.Errorf("ParseCIDR error: %v", err)
 	}
-	_, dstSubnet, err := util.ParseCIDR("5.6.7.8/32")
+	dstSubnet, err := parseCIDR("5.6.7.8/32")
 	if err != nil {
 		t.Errorf("ParseCIDR error: %v", err)
 	}
@@ -474,7 +422,7 @@
 }
 
 func TestNATs(t *testing.T) {
-	_, srcSubnet, err := util.ParseCIDR("1.2.3.4/32")
+	srcSubnet, err := parseCIDR("1.2.3.4/32")
 	if err != nil {
 		t.Errorf("ParseCIDR error: %v", err)
 	}
diff --git a/src/connectivity/network/netstack/filter/nat_test.go b/src/connectivity/network/netstack/filter/nat_test.go
index b13d1a6..f9c24ac 100644
--- a/src/connectivity/network/netstack/filter/nat_test.go
+++ b/src/connectivity/network/netstack/filter/nat_test.go
@@ -19,12 +19,17 @@
 
 // TODO: make these tests table-driven.
 
-var (
-	testLanNet                       = util.Parse("192.168.42.0")
-	testLanNetMask tcpip.AddressMask = "\xff\xff\xff\x00"
+func makeSubnet(addr string, m tcpip.AddressMask) tcpip.Subnet {
+	subnet, err := tcpip.NewSubnet(util.Parse(addr), m)
+	if err != nil {
+		panic(err)
+	}
+	return subnet
+}
 
-	testWanNet                       = util.Parse("10.0.0.0")
-	testWanNetMask tcpip.AddressMask = "\xff\x00\x00\x00"
+var (
+	testLanNet = makeSubnet("192.168.42.0", "\xff\xff\xff\x00")
+	testWanNet = makeSubnet("10.0.0.0", "\xff\x00\x00\x00")
 
 	testLanNICAddr     = util.Parse("192.168.42.10")
 	testRouterNICAddr1 = util.Parse("192.168.42.1")
@@ -58,12 +63,10 @@
 	s.SetRouteTable([]tcpip.Route{
 		{
 			Destination: testLanNet,
-			Mask:        testLanNetMask,
 			NIC:         nic,
 		},
 		{
 			Destination: testWanNet,
-			Mask:        testWanNetMask,
 			NIC:         nic,
 		},
 	})
@@ -82,7 +85,6 @@
 	s.SetRouteTable([]tcpip.Route{
 		{
 			Destination: testWanNet,
-			Mask:        testWanNetMask,
 			NIC:         nic,
 		},
 	})
@@ -93,20 +95,16 @@
 	s := stack.New([]string{ipv4.ProtocolName}, []string{udp.ProtocolName, tcp.ProtocolName}, stack.Options{})
 
 	f := New(s.PortManager)
-	srcSubnet, terr := tcpip.NewSubnet(testLanNet, tcpip.AddressMask(testLanNetMask))
-	if terr != nil {
-		t.Fatalf("NewSubnet error: %s", terr)
-	}
 	f.rulesetNAT.Lock()
 	f.rulesetNAT.v = []NAT{
 		{
 			transProto: header.UDPProtocolNumber,
-			srcSubnet:  &srcSubnet,
+			srcSubnet:  &testLanNet,
 			newSrcAddr: testRouterNICAddr2,
 		},
 		{
 			transProto: header.TCPProtocolNumber,
-			srcSubnet:  &srcSubnet,
+			srcSubnet:  &testLanNet,
 			newSrcAddr: testRouterNICAddr2,
 		},
 	}
@@ -133,12 +131,10 @@
 	s.SetRouteTable([]tcpip.Route{
 		{
 			Destination: testLanNet,
-			Mask:        testLanNetMask,
 			NIC:         nic1,
 		},
 		{
 			Destination: testWanNet,
-			Mask:        testWanNetMask,
 			NIC:         nic2,
 		},
 	})
diff --git a/src/connectivity/network/netstack/filter/rdr_test.go b/src/connectivity/network/netstack/filter/rdr_test.go
index c06c633..926fd04 100644
--- a/src/connectivity/network/netstack/filter/rdr_test.go
+++ b/src/connectivity/network/netstack/filter/rdr_test.go
@@ -60,12 +60,10 @@
 	s.SetRouteTable([]tcpip.Route{
 		{
 			Destination: testLanNet,
-			Mask:        testLanNetMask,
 			NIC:         nic1,
 		},
 		{
 			Destination: testWanNet,
-			Mask:        testWanNetMask,
 			NIC:         nic2,
 		},
 	})
diff --git a/src/connectivity/network/netstack/fuchsia_net_stack.go b/src/connectivity/network/netstack/fuchsia_net_stack.go
index b8204e9..8762f9a 100644
--- a/src/connectivity/network/netstack/fuchsia_net_stack.go
+++ b/src/connectivity/network/netstack/fuchsia_net_stack.go
@@ -138,19 +138,43 @@
 
 	if enabled {
 		if err := ifs.eth.Up(); err != nil {
-			syslog.Errorf("ifs.eth.Up() failed (NIC %d): %v", id, err)
+			syslog.Errorf("ifs.eth.Up() failed (NIC %d): %s", id, err)
 			return &stack.Error{Type: stack.ErrorTypeInternal}
 		}
 	} else {
 		if err := ifs.eth.Down(); err != nil {
-			syslog.Errorf("ifs.eth.Down() failed (NIC %d): %v", id, err)
+			syslog.Errorf("ifs.eth.Down() failed (NIC %d): %s", id, err)
 			return &stack.Error{Type: stack.ErrorTypeInternal}
 		}
 	}
 	return nil
 }
 
+func toProtocolAddr(ifAddr stack.InterfaceAddress) tcpip.ProtocolAddress {
+	protocolAddr := tcpip.ProtocolAddress{
+		AddressWithPrefix: tcpip.AddressWithPrefix{
+			PrefixLen: int(ifAddr.PrefixLen),
+		},
+	}
+
+	switch typ := ifAddr.IpAddress.Which(); typ {
+	case net.IpAddressIpv4:
+		protocolAddr.Protocol = ipv4.ProtocolNumber
+		protocolAddr.AddressWithPrefix.Address = tcpip.Address(ifAddr.IpAddress.Ipv4.Addr[:])
+	case net.IpAddressIpv6:
+		protocolAddr.Protocol = ipv6.ProtocolNumber
+		protocolAddr.AddressWithPrefix.Address = tcpip.Address(ifAddr.IpAddress.Ipv6.Addr[:])
+	default:
+		panic(fmt.Sprintf("unknown IpAddress type %d", typ))
+	}
+	return protocolAddr
+}
+
 func (ns *Netstack) addInterfaceAddr(id uint64, ifAddr stack.InterfaceAddress) *stack.Error {
+	protocolAddr := toProtocolAddr(ifAddr)
+	if protocolAddr.AddressWithPrefix.PrefixLen > 8*len(protocolAddr.AddressWithPrefix.Address) {
+		return &stack.Error{Type: stack.ErrorTypeInvalidArgs}
+	}
 	nicid := tcpip.NICID(id)
 
 	ns.mu.Lock()
@@ -161,44 +185,30 @@
 		return &stack.Error{Type: stack.ErrorTypeNotFound}
 	}
 
-	var protocol tcpip.NetworkProtocolNumber
-	switch typ := ifAddr.IpAddress.Which(); typ {
-	case net.IpAddressIpv4:
-		protocol = ipv4.ProtocolNumber
-	case net.IpAddressIpv6:
-		protocol = ipv6.ProtocolNumber
-	default:
-		panic(fmt.Sprintf("unknown IpAddress type %d", typ))
-	}
-
-	if err := ns.addInterfaceAddress(nicid, protocol, fidlconv.ToTCPIPAddress(ifAddr.IpAddress), ifAddr.PrefixLen); err != nil {
-		syslog.Errorf("(*Netstack).setInterfaceAddress(...) failed (NIC %d): %v", nicid, err)
+	if err := ns.addInterfaceAddress(nicid, protocolAddr); err != nil {
+		syslog.Errorf("(*Netstack).addInterfaceAddr(%s) failed (NIC %d): %s", protocolAddr.AddressWithPrefix, nicid, err)
 		return &stack.Error{Type: stack.ErrorTypeBadState}
 	}
 	return nil
 }
 
-func (ns *Netstack) delInterfaceAddr(id uint64, addr stack.InterfaceAddress) *stack.Error {
+func (ns *Netstack) delInterfaceAddr(id uint64, ifAddr stack.InterfaceAddress) *stack.Error {
+	protocolAddr := toProtocolAddr(ifAddr)
+	if protocolAddr.AddressWithPrefix.PrefixLen > 8*len(protocolAddr.AddressWithPrefix.Address) {
+		return &stack.Error{Type: stack.ErrorTypeInvalidArgs}
+	}
+	nicid := tcpip.NICID(id)
+
 	ns.mu.Lock()
-	_, ok := ns.mu.ifStates[tcpip.NICID(id)]
+	_, ok := ns.mu.ifStates[nicid]
 	ns.mu.Unlock()
 
 	if !ok {
 		return &stack.Error{Type: stack.ErrorTypeNotFound}
 	}
 
-	var protocol tcpip.NetworkProtocolNumber
-	switch tag := addr.IpAddress.Which(); tag {
-	case net.IpAddressIpv4:
-		protocol = ipv4.ProtocolNumber
-	case net.IpAddressIpv6:
-		protocol = ipv6.ProtocolNumber
-	default:
-		panic(fmt.Sprintf("unknown IpAddress type %d", tag))
-	}
-
-	if err := ns.removeInterfaceAddress(tcpip.NICID(id), protocol, fidlconv.ToTCPIPAddress(addr.IpAddress), uint8(addr.PrefixLen)); err != nil {
-		syslog.Errorf("failed to remove interface address: %s", err)
+	if err := ns.removeInterfaceAddress(nicid, protocolAddr); err != nil {
+		syslog.Errorf("(*Netstack).delInterfaceAddr(%s) failed (NIC %d): %s", protocolAddr.AddressWithPrefix, nicid, err)
 		return &stack.Error{Type: stack.ErrorTypeInternal}
 	}
 
@@ -262,13 +272,7 @@
 		return &stack.Error{Type: stack.ErrorTypeInvalidArgs}
 	}
 
-	sn, err := fidlconv.ToTCPIPSubnet(subnet)
-	if err != nil {
-		syslog.Errorf("cannot convert subnet %+v: %s", subnet, err)
-		return &stack.Error{Type: stack.ErrorTypeInvalidArgs}
-	}
-
-	if err := ns.DelRoute(tcpip.Route{Destination: sn.ID(), Mask: sn.Mask()}); err != nil {
+	if err := ns.DelRoute(tcpip.Route{Destination: fidlconv.ToTCPIPSubnet(subnet)}); err != nil {
 		syslog.Errorf("deleting forwarding entry %+v from route table failed: %s", subnet, err)
 		return &stack.Error{Type: stack.ErrorTypeNotFound}
 	}
diff --git a/src/connectivity/network/netstack/fuchsia_net_stack_test.go b/src/connectivity/network/netstack/fuchsia_net_stack_test.go
index 91516f4..81b11ae 100644
--- a/src/connectivity/network/netstack/fuchsia_net_stack_test.go
+++ b/src/connectivity/network/netstack/fuchsia_net_stack_test.go
@@ -6,7 +6,6 @@
 
 import (
 	"fmt"
-	"syscall/zx/fidl"
 	"testing"
 
 	"netstack/fidlconv"
@@ -48,7 +47,6 @@
 }
 
 func TestFuchsiaNetStack(t *testing.T) {
-	go fidl.Serve()
 	t.Run("Add and Delete Forwarding Entries", func(t *testing.T) {
 		ns := newNetstack(t)
 		eth := deviceForAddEth(ethernet.Info{}, t)
diff --git a/src/connectivity/network/netstack/ifconfig/ifconfig.go b/src/connectivity/network/netstack/ifconfig/ifconfig.go
index 7b63938..78b1b24 100644
--- a/src/connectivity/network/netstack/ifconfig/ifconfig.go
+++ b/src/connectivity/network/netstack/ifconfig/ifconfig.go
@@ -150,12 +150,12 @@
 
 func (a *netstackClientApp) newRouteFromArgs(args []string) (route netstack.RouteTableEntry2, err error) {
 	destination, remaining := args[0], args[1:]
-	dstAddr, dstSubnet, err := net.ParseCIDR(destination)
+	_, dstSubnet, err := net.ParseCIDR(destination)
 	if err != nil {
 		return route, fmt.Errorf("invalid destination (destination must be provided in CIDR format): %s", destination)
 	}
 
-	route.Destination = toIpAddress(dstAddr)
+	route.Destination = toIpAddress(dstSubnet.IP)
 	route.Netmask = toIpAddress(net.IP(dstSubnet.Mask))
 
 	for len(remaining) > 0 {
diff --git a/src/connectivity/network/netstack/ifconfig/ifconfig_test.go b/src/connectivity/network/netstack/ifconfig/ifconfig_test.go
index 8f5e4bd..b00ad21 100644
--- a/src/connectivity/network/netstack/ifconfig/ifconfig_test.go
+++ b/src/connectivity/network/netstack/ifconfig/ifconfig_test.go
@@ -49,7 +49,7 @@
 		RunQuietly(t, "/bin/ifconfig", "route", "add", "1.2.3.4/14", "gateway", "9.8.7.6", "iface", "lo")
 
 		out := Run(t, "/bin/ifconfig", "route", "show")
-		expected := "1.2.3.4/14 via 9.8.7.6 lo"
+		expected := "1.0.0.0/14 via 9.8.7.6 lo"
 		if !strings.Contains(out, expected) {
 			t.Errorf("ifconfig route add failed, couldn't find '%s' in '%s'", expected, out)
 		}
diff --git a/src/connectivity/network/netstack/link/bridge/bridge_test.go b/src/connectivity/network/netstack/link/bridge/bridge_test.go
index b4fadcb..9b75dba 100644
--- a/src/connectivity/network/netstack/link/bridge/bridge_test.go
+++ b/src/connectivity/network/netstack/link/bridge/bridge_test.go
@@ -7,6 +7,7 @@
 	"time"
 
 	"netstack/link/bridge"
+	"netstack/util"
 
 	"github.com/google/netstack/tcpip"
 	"github.com/google/netstack/tcpip/buffer"
@@ -64,12 +65,14 @@
 	ep1, ep2 := pipe(tcpip.LinkAddress(bytes.Repeat([]byte{1}, header.EthernetAddressSize)), tcpip.LinkAddress(bytes.Repeat([]byte{2}, header.EthernetAddressSize)))
 	ep3, ep4 := pipe(tcpip.LinkAddress(bytes.Repeat([]byte{3}, header.EthernetAddressSize)), tcpip.LinkAddress(bytes.Repeat([]byte{4}, header.EthernetAddressSize)))
 	s1addr := tcpip.Address([]byte{1, 1, 1, 1})
+	s1subnet := util.PointSubnet(s1addr)
 	s1, err := makeStackWithEndpoint(ep1, s1addr)
 	if err != nil {
 		t.Fatal(err)
 	}
 
 	baddr := tcpip.Address([]byte{2, 2, 2, 2})
+	bsubnet := util.PointSubnet(baddr)
 	sb, b, err := makeStackWithBridgedEndpoints(ep2, ep3, baddr)
 	if err != nil {
 		t.Fatal(err)
@@ -80,6 +83,7 @@
 	}
 
 	s2addr := tcpip.Address([]byte{3, 3, 3, 3})
+	s2subnet := util.PointSubnet(s2addr)
 	s2, err := makeStackWithEndpoint(ep4, s2addr)
 	if err != nil {
 		t.Fatal(err)
@@ -89,6 +93,7 @@
 	// to the address on the virtual NIC representing the bridge itself), to test
 	// that constituent links are still routable.
 	bcaddr := tcpip.Address([]byte{4, 4, 4, 4})
+	bcsubnet := util.PointSubnet(bcaddr)
 	if err := sb.AddAddress(1, header.ARPProtocolNumber, arp.ProtocolAddress); err != nil {
 		t.Fatal(fmt.Errorf("AddAddress failed: %s", err))
 	}
@@ -98,26 +103,22 @@
 
 	s1.SetRouteTable([]tcpip.Route{
 		{
-			Destination: s2addr,
-			Mask:        "\xff\xff\xff\xff",
+			Destination: s2subnet,
 			NIC:         1,
 		},
 		{
-			Destination: baddr,
-			Mask:        "\xff\xff\xff\xff",
+			Destination: bsubnet,
 			NIC:         1,
 		},
 		{
-			Destination: bcaddr,
-			Mask:        "\xff\xff\xff\xff",
+			Destination: bcsubnet,
 			NIC:         1,
 		},
 	})
 
 	sb.SetRouteTable([]tcpip.Route{
 		{
-			Destination: s1addr,
-			Mask:        "\xff\xff\xff\xff",
+			Destination: s1subnet,
 			NIC:         1,
 		},
 	})
@@ -125,8 +126,7 @@
 	s2.SetRouteTable(
 		[]tcpip.Route{
 			{
-				Destination: s1addr,
-				Mask:        "\xff\xff\xff\xff",
+				Destination: s1subnet,
 				NIC:         2,
 			},
 		},
diff --git a/src/connectivity/network/netstack/netstack.go b/src/connectivity/network/netstack/netstack.go
index 27a75bc..50d7a15 100644
--- a/src/connectivity/network/netstack/netstack.go
+++ b/src/connectivity/network/netstack/netstack.go
@@ -7,7 +7,8 @@
 import (
 	"context"
 	"fmt"
-	"strings"
+	"net"
+	"netstack/util"
 	"sync"
 	"sync/atomic"
 	"syscall/zx"
@@ -17,17 +18,14 @@
 
 	"netstack/dhcp"
 	"netstack/dns"
-	"netstack/fidlconv"
 	"netstack/filter"
 	"netstack/link"
 	"netstack/link/bridge"
 	"netstack/link/eth"
 	"netstack/routes"
-	"netstack/util"
 
 	"fidl/fuchsia/device"
 	"fidl/fuchsia/hardware/ethernet"
-	"fidl/fuchsia/net"
 	"fidl/fuchsia/netstack"
 
 	"github.com/google/netstack/tcpip"
@@ -127,23 +125,26 @@
 func defaultRoutes(nicid tcpip.NICID, gateway tcpip.Address) []tcpip.Route {
 	return []tcpip.Route{
 		{
-			Destination: tcpip.Address(strings.Repeat("\x00", 4)),
-			Mask:        tcpip.AddressMask(strings.Repeat("\x00", 4)),
+			Destination: header.IPv4EmptySubnet,
 			Gateway:     gateway,
 			NIC:         nicid,
 		},
 		{
-			Destination: tcpip.Address(strings.Repeat("\x00", 16)),
-			Mask:        tcpip.AddressMask(strings.Repeat("\x00", 16)),
+			Destination: header.IPv6EmptySubnet,
 			NIC:         nicid,
 		},
 	}
 }
 
-func subnetRoute(addr tcpip.Address, mask tcpip.AddressMask, nicid tcpip.NICID) tcpip.Route {
+func addressWithPrefixRoute(nicid tcpip.NICID, addr tcpip.AddressWithPrefix) tcpip.Route {
+	mask := net.CIDRMask(addr.PrefixLen, len(addr.Address)*8)
+	destination, err := tcpip.NewSubnet(tcpip.Address(net.IP(addr.Address).Mask(mask)), tcpip.AddressMask(mask))
+	if err != nil {
+		panic(err)
+	}
+
 	return tcpip.Route{
-		Destination: util.ApplyMask(addr, mask),
-		Mask:        mask,
+		Destination: destination,
 		NIC:         nicid,
 	}
 }
@@ -179,7 +180,7 @@
 	}
 
 	for _, r := range rs {
-		switch len(r.Destination) {
+		switch len(r.Destination.ID()) {
 		case header.IPv4AddressSize, header.IPv6AddressSize:
 		default:
 			// TODO(NET-2244): update this to return an error; panicing here enables syzkaller to find
@@ -264,28 +265,24 @@
 	return nil
 }
 
-func (ns *Netstack) removeInterfaceAddress(nic tcpip.NICID, protocol tcpip.NetworkProtocolNumber, addr tcpip.Address, prefixLen uint8) error {
-	subnet, err := toSubnet(addr, prefixLen)
-	if err != nil {
-		return fmt.Errorf("error parsing subnet format for NIC ID %d: %s", nic, err)
-	}
-	route := subnetRoute(addr, subnet.Mask(), nic)
-	syslog.Infof("removing static IP %s/%d from NIC %d, deleting subnet route %+v", addr, prefixLen, nic, route)
+func (ns *Netstack) removeInterfaceAddress(nic tcpip.NICID, addr tcpip.ProtocolAddress) error {
+	route := addressWithPrefixRoute(nic, addr.AddressWithPrefix)
+	syslog.Infof("removing static IP %s from NIC %d, deleting subnet route %+v", addr.AddressWithPrefix, nic, route)
 
 	ns.mu.Lock()
 	if err := func() error {
-		if _, found := ns.findAddress(nic, protocol, addr); !found {
-			return fmt.Errorf("address %s doesn't exist on NIC ID %d", addr, nic)
+		if _, found := ns.findAddress(nic, addr); !found {
+			return fmt.Errorf("address %s doesn't exist on NIC ID %d", addr.AddressWithPrefix, nic)
 		}
 
 		if err := ns.DelRouteLocked(route); err != nil {
 			// The route might have been removed by user action. Continue.
 		}
 
-		if err := ns.mu.stack.RemoveAddress(nic, addr); err == tcpip.ErrUnknownNICID {
+		if err := ns.mu.stack.RemoveAddress(nic, addr.AddressWithPrefix.Address); err == tcpip.ErrUnknownNICID {
 			panic(fmt.Sprintf("stack.RemoveAddress(_): NIC [%d] not found", nic))
 		} else if err != nil {
-			return fmt.Errorf("error removing address %s from NIC ID %d: %s", addr, nic, err)
+			return fmt.Errorf("error removing address %s from NIC ID %d: %s", addr.AddressWithPrefix, nic, err)
 		}
 
 		return nil
@@ -300,43 +297,25 @@
 	return nil
 }
 
-func toSubnet(address tcpip.Address, prefixLen uint8) (tcpip.Subnet, error) {
-	m := util.CIDRMask(int(prefixLen), int(len(address)*8))
-	if len(m) == 0 {
-		return tcpip.Subnet{}, fmt.Errorf("net.CIDRMask(%d, %d) = nil", prefixLen, len(address)*8)
-	}
-	return tcpip.NewSubnet(util.ApplyMask(address, m), m)
-}
-
-func (ns *Netstack) addInterfaceAddress(nic tcpip.NICID, protocol tcpip.NetworkProtocolNumber, addr tcpip.Address, prefixLen uint8) error {
-	subnet, err := toSubnet(addr, prefixLen)
-	if err != nil {
-		return fmt.Errorf("error parsing subnet format for NIC ID %d: %s", nic, err)
-	}
-	route := subnetRoute(addr, subnet.Mask(), nic)
-	syslog.Infof("adding static IP %v/%d to NIC %d, creating subnet route %+v with metric=<not-set>, dynamic=false", addr, prefixLen, nic, route)
+func (ns *Netstack) addInterfaceAddress(nic tcpip.NICID, addr tcpip.ProtocolAddress) error {
+	route := addressWithPrefixRoute(nic, addr.AddressWithPrefix)
+	syslog.Infof("adding static IP %s to NIC %d, creating subnet route %+v with metric=<not-set>, dynamic=false", addr.AddressWithPrefix, nic, route)
 
 	ns.mu.Lock()
 	if err := func() error {
-		if a, found := ns.findAddress(nic, protocol, addr); found {
-			if int(prefixLen) == a.AddressWithPrefix.PrefixLen {
-				return fmt.Errorf("address %s/%d already exists on NIC ID %d", addr, prefixLen, nic)
+		if a, found := ns.findAddress(nic, addr); found {
+			if a.AddressWithPrefix.PrefixLen == addr.AddressWithPrefix.PrefixLen {
+				return fmt.Errorf("address %s already exists on NIC ID %d", addr.AddressWithPrefix, nic)
 			}
 			// Same address but different prefix. Remove the address and re-add it
 			// with the new prefix (below).
-			if err := ns.mu.stack.RemoveAddress(nic, addr); err != nil {
-				return fmt.Errorf("NIC %d: failed to remove address %s: %s", nic, addr, err)
+			if err := ns.mu.stack.RemoveAddress(nic, addr.AddressWithPrefix.Address); err != nil {
+				return fmt.Errorf("NIC %d: failed to remove address %s: %s", nic, addr.AddressWithPrefix, err)
 			}
 		}
 
-		if err := ns.mu.stack.AddProtocolAddress(nic, tcpip.ProtocolAddress{
-			Protocol: protocol,
-			AddressWithPrefix: tcpip.AddressWithPrefix{
-				Address:   addr,
-				PrefixLen: int(prefixLen),
-			},
-		}); err != nil {
-			return fmt.Errorf("error adding address %s/%d to NIC ID %d: %s", addr, prefixLen, nic, err)
+		if err := ns.mu.stack.AddProtocolAddress(nic, addr); err != nil {
+			return fmt.Errorf("error adding address %s to NIC ID %d: %s", addr.AddressWithPrefix, nic, err)
 		}
 
 		if err := ns.AddRouteLocked(route, metricNotSet, false); err != nil {
@@ -390,7 +369,7 @@
 
 				// Add a default route and a route for the local subnet.
 				rs := defaultRoutes(ifs.nicid, config.Gateway)
-				rs = append(rs, subnetRoute(newAddr.Address, config.SubnetMask, ifs.nicid))
+				rs = append(rs, addressWithPrefixRoute(ifs.nicid, newAddr))
 				syslog.Infof("adding routes %+v with metric=<not-set> dynamic=true", rs)
 
 				if err := ifs.ns.AddRoutesLocked(rs, metricNotSet, true /* dynamic */); err != nil {
@@ -599,13 +578,11 @@
 	if err := ns.AddRoutesLocked(
 		[]tcpip.Route{
 			{
-				Destination: ipv4Loopback,
-				Mask:        tcpip.AddressMask(strings.Repeat("\xff", 4)),
+				Destination: util.PointSubnet(ipv4Loopback),
 				NIC:         nicid,
 			},
 			{
-				Destination: ipv6Loopback,
-				Mask:        tcpip.AddressMask(strings.Repeat("\xff", 16)),
+				Destination: util.PointSubnet(ipv6Loopback),
 				NIC:         nicid,
 			},
 		},
@@ -735,24 +712,6 @@
 	return ifs, nil
 }
 
-func (ns *Netstack) validateInterfaceAddress(address net.IpAddress, prefixLen uint8) (tcpip.NetworkProtocolNumber, tcpip.Address, netstack.NetErr) {
-	var protocol tcpip.NetworkProtocolNumber
-	switch address.Which() {
-	case net.IpAddressIpv4:
-		protocol = ipv4.ProtocolNumber
-	case net.IpAddressIpv6:
-		return 0, "", netstack.NetErr{Status: netstack.StatusIpv4Only, Message: "IPv6 not yet supported"}
-	}
-
-	addr := fidlconv.ToTCPIPAddress(address)
-
-	if (8 * len(addr)) < int(prefixLen) {
-		return 0, "", netstack.NetErr{Status: netstack.StatusParseError, Message: "prefix length exceeds address length"}
-	}
-
-	return protocol, addr, netstack.NetErr{Status: netstack.StatusOk}
-}
-
 func (ns *Netstack) getAddressesLocked(nic tcpip.NICID) []tcpip.ProtocolAddress {
 	nicInfo := ns.mu.stack.NICInfo()
 	info, ok := nicInfo[nic]
@@ -764,10 +723,12 @@
 
 // findAddress finds the given address in the addresses currently assigned to
 // the NIC. Note that no duplicate addresses exist on a NIC.
-func (ns *Netstack) findAddress(nic tcpip.NICID, protocol tcpip.NetworkProtocolNumber, addr tcpip.Address) (tcpip.ProtocolAddress, bool) {
-	addresses := ns.getAddressesLocked(nic)
-	for _, a := range addresses {
-		if a.Protocol == protocol && a.AddressWithPrefix.Address == addr {
+func (ns *Netstack) findAddress(nic tcpip.NICID, addr tcpip.ProtocolAddress) (tcpip.ProtocolAddress, bool) {
+	// Ignore prefix length.
+	addr.AddressWithPrefix.PrefixLen = 0
+	for _, a := range ns.getAddressesLocked(nic) {
+		a.AddressWithPrefix.PrefixLen = 0
+		if a == addr {
 			return a, true
 		}
 	}
diff --git a/src/connectivity/network/netstack/netstack_service.go b/src/connectivity/network/netstack/netstack_service.go
index eefa754..e3ddd5cb 100644
--- a/src/connectivity/network/netstack/netstack_service.go
+++ b/src/connectivity/network/netstack/netstack_service.go
@@ -5,9 +5,10 @@
 package netstack
 
 import (
+	"fidl/fuchsia/net/stack"
 	"fmt"
+	"net"
 	"sort"
-	"strings"
 	"syscall/zx"
 	"syscall/zx/fidl"
 	"syscall/zx/zxwait"
@@ -17,11 +18,10 @@
 	"netstack/fidlconv"
 	"netstack/link"
 	"netstack/routes"
-	"netstack/util"
 
 	"fidl/fuchsia/hardware/ethernet"
 	"fidl/fuchsia/io"
-	"fidl/fuchsia/net"
+	fidlnet "fidl/fuchsia/net"
 	"fidl/fuchsia/netstack"
 
 	"github.com/google/netstack/tcpip"
@@ -86,18 +86,18 @@
 		return netstack.NetInterface2{}, fmt.Errorf("stack.GetMainNICAddress(_): %s", err)
 	}
 
-	mask := util.CIDRMask(addrWithPrefix.PrefixLen, len(addrWithPrefix.Address)*8)
+	mask := net.CIDRMask(addrWithPrefix.PrefixLen, len(addrWithPrefix.Address)*8)
 	broadaddr := []byte(addrWithPrefix.Address)
 	for i := range broadaddr {
 		broadaddr[i] |= ^mask[i]
 	}
 
 	addresses := ifs.ns.getAddressesLocked(ifs.nicid)
-	ipv6addrs := make([]net.Subnet, 0, len(addresses))
+	ipv6addrs := make([]fidlnet.Subnet, 0, len(addresses))
 
 	for _, address := range addresses {
 		if address.Protocol == ipv6.ProtocolNumber {
-			ipv6addrs = append(ipv6addrs, net.Subnet{
+			ipv6addrs = append(ipv6addrs, fidlnet.Subnet{
 				Addr:      fidlconv.ToNetIpAddress(address.AddressWithPrefix.Address),
 				PrefixLen: uint8(address.AddressWithPrefix.PrefixLen),
 			})
@@ -202,7 +202,7 @@
 	rt2 := nsToRouteTable2(ni.ns.GetExtendedRouteTable())
 	rt := make([]netstack.RouteTableEntry, 0, len(rt2))
 	for _, r2 := range rt2 {
-		var gateway net.IpAddress
+		var gateway fidlnet.IpAddress
 		if r2.Gateway != nil {
 			gateway = *r2.Gateway
 		} else {
@@ -218,33 +218,17 @@
 	return rt, nil
 }
 
-func nsToRouteTable2(table []routes.ExtendedRoute) (out []netstack.RouteTableEntry2) {
+func nsToRouteTable2(table []routes.ExtendedRoute) []netstack.RouteTableEntry2 {
+	out := make([]netstack.RouteTableEntry2, 0, len(table))
 	for _, e := range table {
-		// Ensure that if any of the returned addresses are "empty",
-		// they still have the appropriate length.
-		l := 0
-		if len(e.Route.Destination) > 0 {
-			l = len(e.Route.Destination)
-		} else if len(e.Route.Mask) > 0 {
-			l = len(e.Route.Destination)
-		}
-		dest := e.Route.Destination
-		mask := e.Route.Mask
-		if len(dest) == 0 {
-			dest = tcpip.Address(strings.Repeat("\x00", l))
-		}
-		if len(mask) == 0 {
-			mask = tcpip.AddressMask(strings.Repeat("\x00", l))
-		}
-
-		var gatewayPtr *net.IpAddress
+		var gatewayPtr *fidlnet.IpAddress
 		if len(e.Route.Gateway) != 0 {
 			gateway := fidlconv.ToNetIpAddress(e.Route.Gateway)
 			gatewayPtr = &gateway
 		}
 		out = append(out, netstack.RouteTableEntry2{
-			Destination: fidlconv.ToNetIpAddress(dest),
-			Netmask:     fidlconv.ToNetIpAddress(tcpip.Address(mask)),
+			Destination: fidlconv.ToNetIpAddress(e.Route.Destination.ID()),
+			Netmask:     fidlconv.ToNetIpAddress(tcpip.Address(e.Route.Destination.Mask())),
 			Gateway:     gatewayPtr,
 			Nicid:       uint32(e.Route.NIC),
 			Metric:      uint32(e.Metric),
@@ -254,16 +238,18 @@
 }
 
 func routeToNs(r netstack.RouteTableEntry2) tcpip.Route {
-	var gateway tcpip.Address
-	if r.Gateway != nil {
-		gateway = fidlconv.ToTCPIPAddress(*r.Gateway)
+	prefixLen, _ := net.IPMask(fidlconv.ToTCPIPAddress(r.Netmask)).Size()
+	route := tcpip.Route{
+		Destination: fidlconv.ToTCPIPSubnet(fidlnet.Subnet{
+			Addr:      r.Destination,
+			PrefixLen: uint8(prefixLen),
+		}),
+		NIC: tcpip.NICID(r.Nicid),
 	}
-	return tcpip.Route{
-		Destination: fidlconv.ToTCPIPAddress(r.Destination),
-		Mask:        tcpip.AddressMask(fidlconv.ToTCPIPAddress(r.Netmask)),
-		Gateway:     gateway,
-		NIC:         tcpip.NICID(r.Nicid),
+	if g := r.Gateway; g != nil {
+		route.Gateway = fidlconv.ToTCPIPAddress(*g)
 	}
+	return route
 }
 
 type routeTableTransactionImpl struct {
@@ -322,32 +308,33 @@
 }
 
 // Add address to the given network interface.
-func (ni *netstackImpl) SetInterfaceAddress(nicid uint32, address net.IpAddress, prefixLen uint8) (netstack.NetErr, error) {
-	nic := tcpip.NICID(nicid)
-	protocol, addr, neterr := ni.ns.validateInterfaceAddress(address, prefixLen)
-	if neterr.Status != netstack.StatusOk {
-		return neterr, nil
+func (ni *netstackImpl) SetInterfaceAddress(nicid uint32, address fidlnet.IpAddress, prefixLen uint8) (netstack.NetErr, error) {
+	protocolAddr := toProtocolAddr(stack.InterfaceAddress{
+		IpAddress: address,
+		PrefixLen: prefixLen,
+	})
+	if protocolAddr.AddressWithPrefix.PrefixLen > 8*len(protocolAddr.AddressWithPrefix.Address) {
+		return netstack.NetErr{Status: netstack.StatusParseError, Message: "prefix length exceeds address length"}, nil
 	}
-
-	if err := ni.ns.addInterfaceAddress(nic, protocol, addr, prefixLen); err != nil {
+	if err := ni.ns.addInterfaceAddress(tcpip.NICID(nicid), protocolAddr); err != nil {
 		return netstack.NetErr{Status: netstack.StatusUnknownError, Message: err.Error()}, nil
 	}
-	return netstack.NetErr{Status: netstack.StatusOk, Message: ""}, nil
+	return netstack.NetErr{Status: netstack.StatusOk}, nil
 }
 
-func (ni *netstackImpl) RemoveInterfaceAddress(nicid uint32, address net.IpAddress, prefixLen uint8) (netstack.NetErr, error) {
-	nic := tcpip.NICID(nicid)
-	protocol, addr, neterr := ni.ns.validateInterfaceAddress(address, prefixLen)
-
-	if neterr.Status != netstack.StatusOk {
-		return neterr, nil
+func (ni *netstackImpl) RemoveInterfaceAddress(nicid uint32, address fidlnet.IpAddress, prefixLen uint8) (netstack.NetErr, error) {
+	protocolAddr := toProtocolAddr(stack.InterfaceAddress{
+		IpAddress: address,
+		PrefixLen: prefixLen,
+	})
+	if protocolAddr.AddressWithPrefix.PrefixLen > 8*len(protocolAddr.AddressWithPrefix.Address) {
+		return netstack.NetErr{Status: netstack.StatusParseError, Message: "prefix length exceeds address length"}, nil
 	}
-
-	if err := ni.ns.removeInterfaceAddress(nic, protocol, addr, prefixLen); err != nil {
+	if err := ni.ns.removeInterfaceAddress(tcpip.NICID(nicid), protocolAddr); err != nil {
 		return netstack.NetErr{Status: netstack.StatusUnknownError, Message: err.Error()}, nil
 	}
 
-	return netstack.NetErr{Status: netstack.StatusOk, Message: ""}, nil
+	return netstack.NetErr{Status: netstack.StatusOk}, nil
 }
 
 // SetInterfaceMetric updates the metric of an interface.
@@ -449,7 +436,7 @@
 	ns *Netstack
 }
 
-func (dns *dnsImpl) SetNameServers(servers []net.IpAddress) error {
+func (dns *dnsImpl) SetNameServers(servers []fidlnet.IpAddress) error {
 	ss := make([]tcpip.Address, len(servers))
 
 	for i, s := range servers {
diff --git a/src/connectivity/network/netstack/netstack_service_impl_test.go b/src/connectivity/network/netstack/netstack_service_impl_test.go
index c4aa331..0a5c821 100644
--- a/src/connectivity/network/netstack/netstack_service_impl_test.go
+++ b/src/connectivity/network/netstack/netstack_service_impl_test.go
@@ -70,7 +70,7 @@
 			t.Errorf("can't start a transaction")
 		}
 
-		destinationAddress, destinationSubnet, err := net.ParseCIDR("1.2.3.4/24")
+		_, destinationSubnet, err := net.ParseCIDR("1.2.3.4/24")
 		AssertNoError(t, err)
 		gatewayAddress := net.ParseIP("5.6.7.8")
 		if gatewayAddress == nil {
@@ -78,7 +78,7 @@
 		}
 		gateway := toIpAddress(gatewayAddress)
 		newRouteTableEntry2 := netstack.RouteTableEntry2{
-			Destination: toIpAddress(destinationAddress),
+			Destination: toIpAddress(destinationSubnet.IP),
 			Netmask:     toIpAddress(net.IP(destinationSubnet.Mask)),
 			Gateway:     &gateway,
 			Nicid:       uint32(ifs.nicid),
diff --git a/src/connectivity/network/netstack/netstack_test.go b/src/connectivity/network/netstack/netstack_test.go
index e2494e8..61362b9 100644
--- a/src/connectivity/network/netstack/netstack_test.go
+++ b/src/connectivity/network/netstack/netstack_test.go
@@ -5,14 +5,14 @@
 package netstack
 
 import (
+	"net"
 	"sort"
-	"strings"
 	"syscall/zx"
 	"testing"
 	"time"
 
 	"fidl/fuchsia/hardware/ethernet"
-	"fidl/fuchsia/net"
+	fidlnet "fidl/fuchsia/net"
 	"fidl/fuchsia/net/stack"
 	"fidl/fuchsia/netstack"
 	ethernetext "fidlext/fuchsia/hardware/ethernet"
@@ -27,7 +27,6 @@
 	"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"
@@ -372,8 +371,8 @@
 		t.Fatalf("got len(GetInterfaces2()) = %d, want != %d", l, l)
 	}
 
-	var expectedAddr net.IpAddress
-	expectedAddr.SetIpv4(net.Ipv4Address{})
+	var expectedAddr fidlnet.IpAddress
+	expectedAddr.SetIpv4(fidlnet.Ipv4Address{})
 	for _, iface := range interfaces {
 		if iface.Addr != expectedAddr {
 			t.Errorf("got interface %+v, want Addr = %+v", iface, expectedAddr)
@@ -504,15 +503,21 @@
 func TestAddRouteParameterValidation(t *testing.T) {
 	ns := newNetstack(t)
 	d := deviceForAddEth(ethernet.Info{}, t)
-	interfaceAddress, prefix := tcpip.Address("\xf0\xf0\xf0\xf0"), uint8(24)
+	addr := tcpip.ProtocolAddress{
+		Protocol: ipv4.ProtocolNumber,
+		AddressWithPrefix: tcpip.AddressWithPrefix{
+			Address:   tcpip.Address("\xf0\xf0\xf0\xf0"),
+			PrefixLen: 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)
+	if err := ns.addInterfaceAddress(ifState.nicid, addr); err != nil {
+		t.Fatalf("ns.addInterfaceAddress(%d, %s) = %s", ifState.nicid, addr.AddressWithPrefix, err)
 	}
 
 	tests := []struct {
@@ -527,10 +532,15 @@
 			// 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,
+				Destination: func() tcpip.Subnet {
+					subnet, err := tcpip.NewSubnet("", "")
+					if err != nil {
+						t.Fatal(err)
+					}
+					return subnet
+				}(),
+				Gateway: testV4Address,
+				NIC:     ifState.nicid,
 			},
 			metric:      routes.Metric(0),
 			shouldPanic: true,
@@ -539,10 +549,15 @@
 			// 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,
+				Destination: func() tcpip.Subnet {
+					subnet, err := tcpip.NewSubnet("\xff", "\xff")
+					if err != nil {
+						t.Fatal(err)
+					}
+					return subnet
+				}(),
+				Gateway: testV4Address,
+				NIC:     ifState.nicid,
 			},
 			metric:      routes.Metric(0),
 			shouldPanic: true,
@@ -550,8 +565,7 @@
 		{
 			name: "IPv4 destination no NIC invalid gateway",
 			route: tcpip.Route{
-				Destination: testV4Address,
-				Mask:        tcpip.AddressMask(header.IPv4Broadcast),
+				Destination: util.PointSubnet(testV4Address),
 				Gateway:     testV4Address,
 				NIC:         0,
 			},
@@ -561,8 +575,7 @@
 		{
 			name: "IPv6 destination no NIC invalid gateway",
 			route: tcpip.Route{
-				Destination: testV6Address,
-				Mask:        tcpip.AddressMask(strings.Repeat("\xff", 16)),
+				Destination: util.PointSubnet(testV6Address),
 				Gateway:     testV6Address,
 				NIC:         0,
 			},
@@ -572,8 +585,7 @@
 		{
 			name: "IPv4 destination no NIC valid gateway",
 			route: tcpip.Route{
-				Destination: testV4Address,
-				Mask:        tcpip.AddressMask(header.IPv4Broadcast),
+				Destination: util.PointSubnet(testV4Address),
 				Gateway:     subnetLocalAddress,
 				NIC:         0,
 			},
@@ -581,9 +593,7 @@
 		{
 			name: "zero length gateway",
 			route: tcpip.Route{
-				Destination: testV4Address,
-				Mask:        tcpip.AddressMask(header.IPv4Broadcast),
-				Gateway:     tcpip.Address(""),
+				Destination: util.PointSubnet(testV4Address),
 				NIC:         ifState.nicid,
 			},
 		},
@@ -622,6 +632,22 @@
 	gatewayAddress[len(gatewayAddress)-1]++
 	const defaultLeaseLength = 60 * time.Second
 
+	defaultMask := net.IP(testV4Address).DefaultMask()
+	prefixLen, _ := defaultMask.Size()
+
+	destination1, err := tcpip.NewSubnet(util.Parse("192.168.42.0"), tcpip.AddressMask(util.Parse("255.255.255.0")))
+	if err != nil {
+		t.Fatal(err)
+	}
+	destination2, err := tcpip.NewSubnet(util.Parse("0.0.0.0"), tcpip.AddressMask(util.Parse("0.0.0.0")))
+	if err != nil {
+		t.Fatal(err)
+	}
+	destination3, err := tcpip.NewSubnet(util.Parse("::"), tcpip.AddressMask(util.Parse("::")))
+	if err != nil {
+		t.Fatal(err)
+	}
+
 	tests := []struct {
 		name               string
 		oldAddr, newAddr   tcpip.AddressWithPrefix
@@ -633,20 +659,19 @@
 			oldAddr: tcpip.AddressWithPrefix{},
 			newAddr: tcpip.AddressWithPrefix{
 				Address:   testV4Address,
-				PrefixLen: util.PrefixLength(util.DefaultMask(testV4Address)),
+				PrefixLen: prefixLen,
 			},
 			config: dhcp.Config{
 				ServerAddress: tcpip.Address(serverAddress),
 				Gateway:       tcpip.Address(serverAddress),
-				SubnetMask:    util.DefaultMask(testV4Address),
+				SubnetMask:    tcpip.AddressMask(defaultMask),
 				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")),
+						Destination: destination1,
 						NIC:         1,
 					},
 					Metric:                0,
@@ -656,8 +681,7 @@
 				},
 				{
 					Route: tcpip.Route{
-						Destination: util.Parse("0.0.0.0"),
-						Mask:        tcpip.AddressMask(util.Parse("0.0.0.0")),
+						Destination: destination2,
 						Gateway:     util.Parse("192.168.42.18"),
 						NIC:         1,
 					},
@@ -668,8 +692,7 @@
 				},
 				{
 					Route: tcpip.Route{
-						Destination: util.Parse("::"),
-						Mask:        tcpip.AddressMask(util.Parse("::")),
+						Destination: destination3,
 						NIC:         1,
 					},
 					Metric:                0,
@@ -694,7 +717,7 @@
 				t.Errorf("ifState.mu.dnsServers mismatch (-want +got):\n%s", diff)
 			}
 
-			if diff := cmp.Diff(ifState.ns.GetExtendedRouteTable(), test.expectedRouteTable); diff != "" {
+			if diff := cmp.Diff(ifState.ns.GetExtendedRouteTable(), test.expectedRouteTable, cmp.AllowUnexported(tcpip.Subnet{})); diff != "" {
 				t.Errorf("GetExtendedRouteTable() mismatch (-want +got):\n%s", diff)
 			}
 
@@ -751,10 +774,6 @@
 	}
 }
 
-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.
 //
diff --git a/src/connectivity/network/netstack/routes/routes.go b/src/connectivity/network/netstack/routes/routes.go
index 9035728..b302131 100644
--- a/src/connectivity/network/netstack/routes/routes.go
+++ b/src/connectivity/network/netstack/routes/routes.go
@@ -64,32 +64,12 @@
 
 // Match matches the given address against this route.
 func (er *ExtendedRoute) Match(addr tcpip.Address) bool {
-	r := er.Route
-	if len(addr) != len(r.Destination) {
-		return false
-	}
-	for i := 0; i < len(r.Destination); i++ {
-		if (addr[i] & r.Mask[i]) != r.Destination[i] {
-			return false
-		}
-	}
-	return true
-}
-
-// Temporary stringer helper until tcpip.Route implements one.
-func RouteStringer(r *tcpip.Route) string {
-	var out strings.Builder
-	fmt.Fprintf(&out, "%s/%d", r.Destination, util.PrefixLength(r.Mask))
-	if len(r.Gateway) > 0 {
-		fmt.Fprintf(&out, " via %s", r.Gateway)
-	}
-	fmt.Fprintf(&out, " nic %d", r.NIC)
-	return out.String()
+	return er.Route.Destination.Contains(addr)
 }
 
 func (er *ExtendedRoute) String() string {
 	var out strings.Builder
-	fmt.Fprintf(&out, "%s", RouteStringer(&er.Route))
+	fmt.Fprintf(&out, "%s", er.Route)
 	if er.MetricTracksInterface {
 		fmt.Fprintf(&out, " metric[if] %d", er.Metric)
 	} else {
@@ -125,18 +105,12 @@
 	}
 }
 
-func (rt *RouteTable) String() string {
-	rt.mu.Lock()
-	defer rt.mu.Unlock()
-	return rt.mu.routes.String()
-}
-
 // For debugging.
-func (rt *RouteTable) Dump() {
+func (rt *RouteTable) dumpLocked() {
 	if rt == nil {
 		syslog.VLogTf(syslog.TraceVerbosity, tag, "Current Route Table:<nil>")
 	} else {
-		syslog.VLogTf(syslog.TraceVerbosity, tag, "Current Route Table:\n%s", rt)
+		syslog.VLogTf(syslog.TraceVerbosity, tag, "Current Route Table:\n%s", rt.mu.routes)
 	}
 }
 
@@ -151,14 +125,14 @@
 // route already exists, it simply updates that route's metric, dynamic and
 // enabled fields.
 func (rt *RouteTable) AddRoute(route tcpip.Route, metric Metric, tracksInterface bool, dynamic bool, enabled bool) {
-	syslog.VLogTf(syslog.DebugVerbosity, tag, "RouteTable:Adding route %s with metric:%d, trackIf=%t, dynamic=%t, enabled=%t", RouteStringer(&route), metric, tracksInterface, dynamic, enabled)
+	syslog.VLogTf(syslog.DebugVerbosity, tag, "RouteTable:Adding route %s with metric:%d, trackIf=%t, dynamic=%t, enabled=%t", route, metric, tracksInterface, dynamic, enabled)
 
 	rt.mu.Lock()
 	defer rt.mu.Unlock()
 
 	// First check if the route already exists, and remove it.
 	for i, er := range rt.mu.routes {
-		if IsSameRoute(route, er.Route) {
+		if er.Route == route {
 			rt.mu.routes = append(rt.mu.routes[:i], rt.mu.routes[i+1:]...)
 			break
 		}
@@ -189,12 +163,12 @@
 		rt.mu.routes[targetIdx] = newEr
 	}
 
-	rt.Dump()
+	rt.dumpLocked()
 }
 
 // DelRoute removes the given route from the route table.
 func (rt *RouteTable) DelRoute(route tcpip.Route) error {
-	syslog.VLogTf(syslog.DebugVerbosity, tag, "RouteTable:Deleting route %s", RouteStringer(&route))
+	syslog.VLogTf(syslog.DebugVerbosity, tag, "RouteTable:Deleting route %s", route)
 
 	rt.mu.Lock()
 	defer rt.mu.Unlock()
@@ -204,7 +178,7 @@
 	rt.mu.routes = oldTable[:0]
 	for _, er := range oldTable {
 		// Match all fields that are non-zero.
-		if isSameSubnet(er.Route, route) {
+		if er.Route.Destination == route.Destination {
 			if route.NIC == 0 || route.NIC == er.Route.NIC {
 				if len(route.Gateway) == 0 || route.Gateway == er.Route.Gateway {
 					routeDeleted = true
@@ -220,7 +194,7 @@
 		return fmt.Errorf("no such route")
 	}
 
-	rt.Dump()
+	rt.dumpLocked()
 	return nil
 }
 
@@ -229,7 +203,7 @@
 	rt.mu.Lock()
 	defer rt.mu.Unlock()
 
-	rt.Dump()
+	rt.dumpLocked()
 
 	return append([]ExtendedRoute(nil), rt.mu.routes...)
 }
@@ -269,7 +243,7 @@
 
 	rt.sortRouteTableLocked()
 
-	rt.Dump()
+	rt.dumpLocked()
 }
 
 // UpdateRoutesByInterface applies an action to the routes pointing to an interface.
@@ -306,7 +280,7 @@
 
 	rt.sortRouteTableLocked()
 
-	rt.Dump()
+	rt.dumpLocked()
 }
 
 // FindNIC returns the NIC-ID that the given address is routed on. This requires
@@ -317,7 +291,7 @@
 
 	for _, er := range rt.mu.routes {
 		// Ignore default routes.
-		if util.IsAny(er.Route.Destination) {
+		if util.IsAny(er.Route.Destination.ID()) {
 			continue
 		}
 		if er.Match(addr) && er.Route.NIC > 0 {
@@ -337,20 +311,20 @@
 // route table.
 func Less(ei, ej *ExtendedRoute) bool {
 	ri, rj := ei.Route, ej.Route
+	riDest, rjDest := ri.Destination.ID(), rj.Destination.ID()
 	// Non-default before default one.
-	if util.IsAny(ri.Destination) != util.IsAny(rj.Destination) {
-		return !util.IsAny(ri.Destination)
+	if riAny, rjAny := util.IsAny(riDest), util.IsAny(rjDest); riAny != rjAny {
+		return !riAny
 	}
 
 	// IPv4 before IPv6 (arbitrary choice).
-	if len(ri.Destination) != len(rj.Destination) {
-		return len(ri.Destination) == header.IPv4AddressSize
+	if riLen, rjLen := len(riDest), len(rjDest); riLen != rjLen {
+		return riLen == header.IPv4AddressSize
 	}
 
 	// Longer prefix wins.
-	li, lj := util.PrefixLength(ri.Mask), util.PrefixLength(rj.Mask)
-	if len(ri.Mask) == len(rj.Mask) && li != lj {
-		return li > lj
+	if riPrefix, rjPrefix := ri.Destination.Prefix(), rj.Destination.Prefix(); riPrefix != rjPrefix {
+		return riPrefix > rjPrefix
 	}
 
 	// Lower metrics wins.
@@ -361,7 +335,6 @@
 	// Everything that matters is the same. At this point we still need a
 	// deterministic way to tie-break. First go by destination IPs (lower wins),
 	// finally use the NIC.
-	riDest, rjDest := []byte(ri.Destination), []byte(rj.Destination)
 	for i := 0; i < len(riDest); i++ {
 		if riDest[i] != rjDest[i] {
 			return riDest[i] < rjDest[i]
@@ -372,22 +345,3 @@
 	// tie-breaker.
 	return ri.NIC < rj.NIC
 }
-
-func isSameSubnet(a, b tcpip.Route) bool {
-	return a.Destination == b.Destination && a.Mask == b.Mask
-}
-
-// IsSameRoute returns true if two routes are the same.
-func IsSameRoute(a, b tcpip.Route) bool {
-	if !isSameSubnet(a, b) || a.NIC != b.NIC {
-		return false
-	}
-
-	aHasGW := len(a.Gateway) > 0 && !util.IsAny(a.Gateway)
-	bHasGW := len(a.Gateway) > 0 && !util.IsAny(b.Gateway)
-	if aHasGW && bHasGW {
-		return a.Gateway == b.Gateway
-	}
-	// either one or both routes have no gateway
-	return !aHasGW && !bHasGW
-}
diff --git a/src/connectivity/network/netstack/routes/routes_test.go b/src/connectivity/network/netstack/routes/routes_test.go
index 2dcf2c7..d45f7f2 100644
--- a/src/connectivity/network/netstack/routes/routes_test.go
+++ b/src/connectivity/network/netstack/routes/routes_test.go
@@ -10,7 +10,6 @@
 	"testing"
 
 	"netstack/routes"
-	"netstack/util"
 
 	"github.com/google/netstack/tcpip"
 )
@@ -32,10 +31,16 @@
 }
 
 func createRoute(nicid tcpip.NICID, subnet string, gateway string) tcpip.Route {
-	_, s, _ := net.ParseCIDR(subnet)
+	_, s, err := net.ParseCIDR(subnet)
+	if err != nil {
+		panic(err)
+	}
+	sn, err := tcpip.NewSubnet(tcpip.Address(s.IP), tcpip.AddressMask(s.Mask))
+	if err != nil {
+		panic(err)
+	}
 	return tcpip.Route{
-		Destination: tcpip.Address(s.IP),
-		Mask:        tcpip.AddressMask(s.Mask),
+		Destination: sn,
 		Gateway:     ipStringToAddress(gateway),
 		NIC:         nicid,
 	}
@@ -178,73 +183,13 @@
 	}
 }
 
-func changeByte(b []byte) []byte {
-	b[0] = ^b[0]
-	return b
-}
-
-func TestIsSameRoute(t *testing.T) {
-	gatewaysWithPrefix := []string{"127.0.0.1/32", "0.0.0.0/0", "123.220.3.14/14", "192.168.10.1/24",
-		"::1/128", "::/128", "2605:143:32:113::1/64", "fe80:4234:242f:1111:5:243:5:4f/88"}
-	nics := []tcpip.NICID{1, 2}
-
-	for _, nic1 := range nics {
-		t.Run(fmt.Sprintf("nic1=%d", nic1), func(t *testing.T) {
-			for _, gp1 := range gatewaysWithPrefix {
-				t.Run(fmt.Sprintf("gp1=%s", gp1), func(t *testing.T) {
-					ip, s, err := net.ParseCIDR(gp1)
-					if err != nil {
-						t.Fatalf("net.ParseCIDR(%s) failed", gp1)
-					}
-					route1 := tcpip.Route{
-						Destination: tcpip.Address(ipToAddress(s.IP)),
-						Mask:        tcpip.AddressMask(s.Mask),
-						Gateway:     tcpip.Address(ipToAddress(ip)),
-						NIC:         nic1,
-					}
-
-					if got := routes.IsSameRoute(route1, route1); got != true {
-						t.Fatalf("got IsSameRoute(%s, %s) = %t, want = %t", routes.RouteStringer(&route1), routes.RouteStringer(&route1), got, true)
-					}
-					// Change the NIC
-					route2 := route1
-					route2.NIC = route1.NIC + 1
-					if got := routes.IsSameRoute(route1, route2); got != false {
-						t.Errorf("got IsSameRoute(%s, %s) = %t, want = %t", routes.RouteStringer(&route1), routes.RouteStringer(&route2), got, false)
-					}
-					// Change destination.
-					route2 = route1
-					route2.Destination = tcpip.Address(changeByte([]byte(route2.Destination)))
-					if got := routes.IsSameRoute(route1, route2); got != false {
-						t.Errorf("got IsSameRoute(%s, %s) = %t, want = %t", routes.RouteStringer(&route1), routes.RouteStringer(&route2), got, false)
-					}
-					// Change gateway.
-					route2 = route1
-					route2.Gateway = tcpip.Address(changeByte([]byte(route2.Gateway)))
-					if got := routes.IsSameRoute(route1, route2); got != false {
-						t.Errorf("got IsSameRoute(%s, %s) = %t, want = %t", routes.RouteStringer(&route1), routes.RouteStringer(&route2), got, false)
-					}
-					// Remove gateway if possible
-					if !util.IsAny(route1.Gateway) {
-						route2 = route1
-						route2.Gateway = tcpip.Address("")
-						if got := routes.IsSameRoute(route1, route2); got != false {
-							t.Errorf("got IsSameRoute(%s, %s) = %t, want = %t", routes.RouteStringer(&route1), routes.RouteStringer(&route2), got, false)
-						}
-					}
-				})
-			}
-		})
-	}
-}
-
 func isSameRouteTableImpl(rt1, rt2 []routes.ExtendedRoute, checkAttributes bool) bool {
 	if len(rt1) != len(rt2) {
 		return false
 	}
 	for i, r1 := range rt1 {
 		r2 := rt2[i]
-		if !routes.IsSameRoute(r1.Route, r2.Route) {
+		if r1.Route != r2.Route {
 			return false
 		}
 		if checkAttributes && (r1.Metric != r2.Metric || r1.MetricTracksInterface != r2.MetricTracksInterface || r1.Dynamic != r2.Dynamic || r1.Enabled != r2.Enabled) {
@@ -333,21 +278,31 @@
 		r1 := createRoute(2, "0.0.0.0/0", "192.168.100.10")
 
 		// 1.test - r0 gets lower metric.
-		tb := routes.RouteTable{}
-		tb.AddRoute(r0, 100, true, true, true)
-		tb.AddRoute(r1, 200, true, true, true)
-		tableGot := tb.GetExtendedRouteTable()
-		if !routes.IsSameRoute(r0, tableGot[0].Route) || !routes.IsSameRoute(r1, tableGot[1].Route) {
-			t.Errorf("got %s, %s, want %s, %s", routes.RouteStringer(&tableGot[0].Route), routes.RouteStringer(&tableGot[1].Route), routes.RouteStringer(&r0), routes.RouteStringer(&r1))
+		{
+			var tb routes.RouteTable
+			tb.AddRoute(r0, 100, true, true, true)
+			tb.AddRoute(r1, 200, true, true, true)
+			tableGot := tb.GetExtendedRouteTable()
+			if got, want := tableGot[0].Route, r0; got != want {
+				t.Errorf("got = %s, want = %s", got, want)
+			}
+			if got, want := tableGot[1].Route, r1; got != want {
+				t.Errorf("got = %s, want = %s", got, want)
+			}
 		}
 
 		// 2.test - r1 gets lower metric.
-		tb = routes.RouteTable{}
-		tb.AddRoute(r0, 200, true, true, true)
-		tb.AddRoute(r1, 100, true, true, true)
-		tableGot = tb.GetExtendedRouteTable()
-		if !routes.IsSameRoute(r0, tableGot[1].Route) || !routes.IsSameRoute(r1, tableGot[0].Route) {
-			t.Errorf("got %s, %s, want %s, %s", routes.RouteStringer(&tableGot[0].Route), routes.RouteStringer(&tableGot[1].Route), routes.RouteStringer(&r1), routes.RouteStringer(&r0))
+		{
+			var tb routes.RouteTable
+			tb.AddRoute(r0, 200, true, true, true)
+			tb.AddRoute(r1, 100, true, true, true)
+			tableGot := tb.GetExtendedRouteTable()
+			if got, want := tableGot[0].Route, r1; got != want {
+				t.Errorf("got = %s, want = %s", got, want)
+			}
+			if got, want := tableGot[1].Route, r0; got != want {
+				t.Errorf("got = %s, want = %s", got, want)
+			}
 		}
 	})
 }
@@ -434,17 +389,27 @@
 		tb := routes.RouteTable{}
 		tb.AddRoute(r0, 100, true, true, true)
 		tb.AddRoute(r1, 200, true, true, true)
-		tableGot := tb.GetExtendedRouteTable()
-		if !routes.IsSameRoute(r0, tableGot[0].Route) || !routes.IsSameRoute(r1, tableGot[1].Route) {
-			t.Errorf("got %s, %s, want %s, %s", routes.RouteStringer(&tableGot[0].Route), routes.RouteStringer(&tableGot[1].Route), routes.RouteStringer(&r0), routes.RouteStringer(&r1))
+		{
+			tableGot := tb.GetExtendedRouteTable()
+			if got, want := tableGot[0].Route, r0; got != want {
+				t.Errorf("got = %s, want = %s", got, want)
+			}
+			if got, want := tableGot[1].Route, r1; got != want {
+				t.Errorf("got = %s, want = %s", got, want)
+			}
 		}
 
 		// Lowering nic1's metric should be reflected in r1's metric and promote it
 		// in the route table.
 		tb.UpdateMetricByInterface(1, 50)
-		tableGot = tb.GetExtendedRouteTable()
-		if !routes.IsSameRoute(r0, tableGot[1].Route) || !routes.IsSameRoute(r1, tableGot[0].Route) {
-			t.Errorf("got %s, %s, want %s, %s", routes.RouteStringer(&tableGot[0].Route), routes.RouteStringer(&tableGot[1].Route), routes.RouteStringer(&r1), routes.RouteStringer(&r0))
+		{
+			tableGot := tb.GetExtendedRouteTable()
+			if got, want := tableGot[0].Route, r1; got != want {
+				t.Errorf("got = %s, want = %s", got, want)
+			}
+			if got, want := tableGot[1].Route, r0; got != want {
+				t.Errorf("got = %s, want = %s", got, want)
+			}
 		}
 	})
 }
@@ -561,25 +526,24 @@
 		t.Run(tc.name, func(t *testing.T) {
 			// We have no way to directly disable routes in the route table, but we
 			// can use the Set() command to set a table with pre-disabled routes.
-			testRouteTable2 := make([]routes.ExtendedRoute, len(testRouteTable))
-			copy(testRouteTable2, testRouteTable)
+			testRouteTable := append([]routes.ExtendedRoute(nil), testRouteTable...)
 			// Disable a few routes.
 			for _, i := range tc.disabled {
-				testRouteTable2[i].Enabled = false
+				testRouteTable[i].Enabled = false
 			}
-			tb := routes.RouteTable{}
-			tb.Set(testRouteTable2)
+			var tb routes.RouteTable
+			tb.Set(testRouteTable)
 
 			tableGot := tb.GetNetstackTable()
 
 			// Verify no disabled routes are in the Netstack table we got.
 			i := 0
-			for _, r := range testRouteTable2 {
+			for _, r := range testRouteTable {
 				if r.Enabled {
-					if !routes.IsSameRoute(tableGot[i], r.Route) {
-						t.Errorf("got = %s, want = %s", routes.RouteStringer(&tableGot[i]), routes.RouteStringer(&r.Route))
+					if got, want := tableGot[i], r.Route; got != want {
+						t.Errorf("got = %s, want = %s", got, want)
 					}
-					i += 1
+					i++
 				}
 			}
 		})
diff --git a/src/connectivity/network/netstack/socket_server.go b/src/connectivity/network/netstack/socket_server.go
index 3b973ee..af3cd69 100644
--- a/src/connectivity/network/netstack/socket_server.go
+++ b/src/connectivity/network/netstack/socket_server.go
@@ -170,7 +170,7 @@
 				return fmt.Errorf("Endpoint.Write(%s): %s", optsStr, err)
 			}
 			if ios.transProto != tcp.ProtocolNumber {
-				if int(n) < len(v) {
+				if n < int64(len(v)) {
 					panic(fmt.Sprintf("UDP disallows short writes; saw: %d/%d", n, len(v)))
 				}
 			}
@@ -717,21 +717,30 @@
 	if err != nil {
 		return tcpipErrorToCode(tcpip.ErrBadAddress), nil
 	}
-	if l := len(addr.Addr); l > 0 {
-		if ios.netProto == ipv4.ProtocolNumber && l != header.IPv4AddressSize {
-			syslog.VLogTf(syslog.DebugVerbosity, "connect", "%p: unsupported address %s", ios, addr.Addr)
-			return C.EAFNOSUPPORT, nil
+	// NB: We can't just compare the length to zero because that would
+	// mishandle the IPv6-mapped IPv4 unspecified address.
+	disconnect := addr.Port == 0 && (len(addr.Addr) == 0 || net.IP(addr.Addr).IsUnspecified())
+	if disconnect {
+		if err := ios.ep.Disconnect(); err != nil {
+			return tcpipErrorToCode(err), nil
 		}
-	}
-	if err := ios.ep.Connect(addr); err != nil {
-		if err == tcpip.ErrConnectStarted {
-			localAddr, err := ios.ep.GetLocalAddress()
-			if err != nil {
-				panic(err)
+	} else {
+		if l := len(addr.Addr); l > 0 {
+			if ios.netProto == ipv4.ProtocolNumber && l != header.IPv4AddressSize {
+				syslog.VLogTf(syslog.DebugVerbosity, "connect", "%p: unsupported address %s", ios, addr.Addr)
+				return C.EAFNOSUPPORT, nil
 			}
-			syslog.VLogTf(syslog.DebugVerbosity, "connect", "%p: started, local=%+v, addr=%+v", ios, localAddr, addr)
 		}
-		return tcpipErrorToCode(err), nil
+		if err := ios.ep.Connect(addr); err != nil {
+			if err == tcpip.ErrConnectStarted {
+				localAddr, err := ios.ep.GetLocalAddress()
+				if err != nil {
+					panic(err)
+				}
+				syslog.VLogTf(syslog.DebugVerbosity, "connect", "%p: started, local=%+v, addr=%+v", ios, localAddr, addr)
+			}
+			return tcpipErrorToCode(err), nil
+		}
 	}
 
 	{
@@ -740,9 +749,7 @@
 			panic(err)
 		}
 
-		// NB: We can't just compare the length to zero because that would
-		// mishandle the IPv6-mapped IPv4 unspecified address.
-		if len(addr.Addr) == 0 || net.IP(addr.Addr).IsUnspecified() {
+		if disconnect {
 			syslog.VLogTf(syslog.DebugVerbosity, "connect", "%p: local=%+v, remote=disconnected", ios, localAddr)
 		} else {
 			remoteAddr, err := ios.ep.GetRemoteAddress()
diff --git a/src/connectivity/network/netstack/util/parse.go b/src/connectivity/network/netstack/util/parse.go
index aac22b8..b43a8ad 100644
--- a/src/connectivity/network/netstack/util/parse.go
+++ b/src/connectivity/network/netstack/util/parse.go
@@ -6,7 +6,6 @@
 package util
 
 import (
-	"fmt"
 	"net"
 
 	"github.com/google/netstack/tcpip"
@@ -25,51 +24,21 @@
 	return true
 }
 
-func ApplyMask(addr tcpip.Address, mask tcpip.AddressMask) tcpip.Address {
-	result := net.IP(addr).Mask(net.IPMask(mask))
-	if result == nil {
-		panic(fmt.Sprintf("net.IP(%s).Mask(%s) = nil", addr, mask))
-	}
-	return tcpip.Address(result)
-}
-
-func DefaultMask(addr tcpip.Address) tcpip.AddressMask {
-	result := net.IP(addr).DefaultMask()
-	if result == nil {
-		panic(fmt.Sprintf("net.IP(%s).DefaultMask() = nil", addr))
-	}
-	return tcpip.AddressMask(result)
-}
-
-func CIDRMask(ones, bits int) tcpip.AddressMask {
-	return tcpip.AddressMask(net.CIDRMask(ones, bits))
-}
-
-func PrefixLength(mask tcpip.AddressMask) int {
-	bits, _ := net.IPMask(mask).Size()
-	return bits
-}
-
-func ipToAddress(ip net.IP) tcpip.Address {
+// Parse parses the string representation of an IPv4 or IPv6 address.
+func Parse(src string) tcpip.Address {
+	ip := net.ParseIP(src)
 	if v4 := ip.To4(); v4 != nil {
 		return tcpip.Address(v4)
 	}
 	return tcpip.Address(ip)
 }
 
-func ParseCIDR(s string) (tcpip.Address, tcpip.Subnet, error) {
-	ip, subnet, err := net.ParseCIDR(s)
+// PointSubnet creates a subnet which contains only the passed address.
+func PointSubnet(a tcpip.Address) tcpip.Subnet {
+	l := len(a) * 8
+	subnet, err := tcpip.NewSubnet(a, tcpip.AddressMask(net.CIDRMask(l, l)))
 	if err != nil {
-		return "", tcpip.Subnet{}, err
+		panic(err)
 	}
-	sn, err := tcpip.NewSubnet(tcpip.Address(subnet.IP), tcpip.AddressMask(subnet.Mask))
-	if err != nil {
-		return "", tcpip.Subnet{}, err
-	}
-	return ipToAddress(ip), sn, nil
-}
-
-// Parse parses the string representation of an IPv4 or IPv6 address.
-func Parse(src string) tcpip.Address {
-	return ipToAddress(net.ParseIP(src))
+	return subnet
 }
diff --git a/src/connectivity/network/netstack/util/parse_test.go b/src/connectivity/network/netstack/util/parse_test.go
index 84371c7..f36c930 100644
--- a/src/connectivity/network/netstack/util/parse_test.go
+++ b/src/connectivity/network/netstack/util/parse_test.go
@@ -9,56 +9,6 @@
 	"github.com/google/netstack/tcpip"
 )
 
-func TestApplyMask(t *testing.T) {
-	tests := []struct {
-		name        string
-		addr        tcpip.Address
-		mask        tcpip.AddressMask
-		want        tcpip.Address
-		shouldPanic bool
-	}{
-		{
-			name: "fullMask",
-			addr: "\x01\x01\x01\x01",
-			mask: "\xff\xff\xff\xff",
-			want: "\x01\x01\x01\x01",
-		},
-		{
-			name: "partialMask",
-			addr: "\x01\x01\x01\x01",
-			mask: "\xff\xff\xff\x00",
-			want: "\x01\x01\x01\x00",
-		},
-		{
-			name:        "mismatchedLengths",
-			addr:        "\x01\x01\x01\x01",
-			mask:        "\xff\xff\xff",
-			shouldPanic: true,
-		},
-		{
-			name:        "zeroLengthMask",
-			addr:        "\x01\x01\x01\x01",
-			mask:        "",
-			shouldPanic: true,
-		},
-	}
-
-	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() = %s", r)
-					t.Errorf("got (recover() != nil) = %t; want = %t", got, test.shouldPanic)
-				}
-			}()
-			if got := util.ApplyMask(test.addr, test.mask); got != test.want {
-				t.Errorf("got util.ApplyMask(%s, %s) = %s, want = %s", test.addr, test.mask, got, test.want)
-			}
-		})
-	}
-}
-
 func TestParse(t *testing.T) {
 	tests := []struct {
 		txt  string
@@ -90,65 +40,6 @@
 	}
 }
 
-func TestDefaultMask(t *testing.T) {
-	tests := []struct {
-		addr        tcpip.Address
-		mask        tcpip.AddressMask
-		shouldPanic bool
-	}{
-		{addr: util.Parse("192.168.42.10"), mask: "\xff\xff\xff\x00"},
-		{addr: util.Parse("10.0.1.1"), mask: "\xff\x00\x00\x00"},
-		{addr: util.Parse("10.0.1"), shouldPanic: true},
-		{addr: util.Parse("2001:0db8:85a3:0000:0000:8a2e:0370:7334"), shouldPanic: true},
-		{addr: util.Parse(""), shouldPanic: true},
-	}
-
-	for _, test := range tests {
-		func() {
-			defer func() {
-				r := recover()
-				if got := r != nil; got != test.shouldPanic {
-					t.Logf("recover() = %s", r)
-					t.Errorf("got (recover() != nil) = %t; want = %t", got, test.shouldPanic)
-				}
-			}()
-			got := util.DefaultMask(test.addr)
-			if got != test.mask {
-				t.Errorf("got util.DefaultMask(%q) = %q, want %q", test.addr, got, test.mask)
-			}
-		}()
-	}
-}
-
-// Copied from pkg net (ip_test.go).
-func TestParseCIDR(t *testing.T) {
-	for _, tt := range []struct {
-		in      string
-		address string
-		netmask string
-	}{
-		{"135.104.0.0/32", "135.104.0.0", "255.255.255.255"},
-		{"0.0.0.0/24", "0.0.0.0", "255.255.255.0"},
-		{"135.104.0.0/24", "135.104.0.0", "255.255.255.0"},
-		{"135.104.0.1/32", "135.104.0.1", "255.255.255.255"},
-		{"135.104.0.1/24", "135.104.0.1", "255.255.255.0"},
-	} {
-		address, subnet, err := util.ParseCIDR(tt.in)
-		if err != nil {
-			t.Error(err)
-		} else if want := util.Parse(tt.address); address != want {
-			t.Errorf("ParseCIDR('%s') = ('%s', _); want ('%s', _)", tt.in, address, want)
-		} else {
-			netmask := tcpip.AddressMask(util.Parse(tt.netmask))
-			if want, err := tcpip.NewSubnet(util.ApplyMask(want, netmask), netmask); err != nil {
-				t.Errorf("tcpip.NewSubnet('%v', '%v') failed: %v", want, tt.netmask, err)
-			} else if want != subnet {
-				t.Errorf("ParseCIDR('%s') = (_, %+v); want (_, %+v)", tt.in, subnet, want)
-			}
-		}
-	}
-}
-
 func TestIsAny(t *testing.T) {
 	for _, tc := range []struct {
 		name string
@@ -182,36 +73,3 @@
 		})
 	}
 }
-
-func TestPrefixLength(t *testing.T) {
-	for _, tc := range []struct {
-		name string
-		mask tcpip.AddressMask
-		want int
-	}{
-		{"IPv4-Empty", "", 0},
-		{"IPv4-0", "\x00\x00\x00\x00", 0},
-		{"IPv4-3", "\xe0\x00\x00\x00", 3},
-		{"IPv4-7", "\xfe\x00\x00\x00", 7},
-		{"IPv4-8", "\xff\x00\x00\x00", 8},
-		{"IPv4-12", "\xff\xf0\x00\x00", 12},
-		{"IPv4-16", "\xff\xff\x00\x00", 16},
-		{"IPv4-24", "\xff\xff\xff\x00", 24},
-		{"IPv4-29", "\xff\xff\xff\xfc", 30},
-		{"IPv4-32", "\xff\xff\xff\xff", 32},
-		{"IPv6-Empty", "", 0},
-		{"IPv6-0", "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0},
-		{"IPv6-5", "\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 5},
-		{"IPv6-8", "\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 8},
-		{"IPv6-22", "\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 22},
-		{"IPv6-73", "\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00", 73},
-		{"IPv6-123", "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0", 123},
-		{"IPv6-128", "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", 128},
-	} {
-		t.Run(tc.name, func(t *testing.T) {
-			if got := util.PrefixLength(tc.mask); got != tc.want {
-				t.Fatalf("PrefixLength(%v) = %v, want = %v", tc.mask, got, tc.want)
-			}
-		})
-	}
-}
diff --git a/src/connectivity/network/tests/src/lib.rs b/src/connectivity/network/tests/src/lib.rs
index 0e5a4d8..ea95ace 100644
--- a/src/connectivity/network/tests/src/lib.rs
+++ b/src/connectivity/network/tests/src/lib.rs
@@ -43,14 +43,16 @@
     name: &'static str,
     endpoint_manager: &'a fidl_fuchsia_netemul_network::EndpointManagerProxy,
 ) -> std::result::Result<fidl_fuchsia_netemul_network::EndpointProxy, failure::Error> {
-    let (status, endpoint) = endpoint_manager.create_endpoint(
-        name,
-        &mut fidl_fuchsia_netemul_network::EndpointConfig {
-            mtu: 1500,
-            mac: None,
-            backing: fidl_fuchsia_netemul_network::EndpointBacking::Ethertap,
-        },
-    ).await
+    let (status, endpoint) = endpoint_manager
+        .create_endpoint(
+            name,
+            &mut fidl_fuchsia_netemul_network::EndpointConfig {
+                mtu: 1500,
+                mac: None,
+                backing: fidl_fuchsia_netemul_network::EndpointBacking::Ethertap,
+            },
+        )
+        .await
         .context("failed to create endpoint")?;
     let () = fuchsia_zircon::Status::ok(status).context("failed to create endpoint")?;
     let endpoint = endpoint
@@ -161,8 +163,7 @@
     let (client, server) = fidl::endpoints::create_proxy::<fidl_fuchsia_io::DirectoryMarker>()
         .context("failed to create node proxy")?;
     let () = netstack_proxy.get_aggregate_stats(server).context("failed to get aggregate stats")?;
-    let dir_entries =
-        files_async::readdir_recursive(&client).await.context("failed to readdir")?;
+    let dir_entries = files_async::readdir_recursive(&client).await.context("failed to readdir")?;
     let path_segments: std::collections::hash_set::HashSet<usize> = dir_entries
         .iter()
         .map(|dir_entry| 1 + dir_entry.name.matches(std::path::MAIN_SEPARATOR).count())
@@ -181,18 +182,22 @@
         name,
         |netstack, device| {
             async move {
-                let id = netstack.add_ethernet_device(
-                    name,
-                    &mut fidl_fuchsia_netstack::InterfaceConfig {
-                        name: name.to_string(),
-                        filepath: "/fake/filepath/for_test".to_string(),
-                        metric: 0,
-                        ip_address_config: fidl_fuchsia_netstack::IpAddressConfig::Dhcp(true),
-                    },
-                    device,
-                ).await
+                let id = netstack
+                    .add_ethernet_device(
+                        name,
+                        &mut fidl_fuchsia_netstack::InterfaceConfig {
+                            name: name.to_string(),
+                            filepath: "/fake/filepath/for_test".to_string(),
+                            metric: 0,
+                            ip_address_config: fidl_fuchsia_netstack::IpAddressConfig::Dhcp(true),
+                        },
+                        device,
+                    )
+                    .await
                     .context("failed to add ethernet device")?;
-                let interface = netstack.get_interfaces2().await
+                let interface = netstack
+                    .get_interfaces2()
+                    .await
                     .context("failed to get interfaces")?
                     .into_iter()
                     .find(|interface| interface.id == id)
@@ -205,38 +210,41 @@
                 Ok::<(), failure::Error>(())
             }
         },
-    ).await
+    )
+    .await
 }
 
 #[fuchsia_async::run_singlethreaded(test)]
 async fn add_ethernet_interface() -> Result {
     let name = stringify!(add_ethernet_interface);
 
-    with_netstack_and_device::<_, _, fidl_fuchsia_net_stack::StackMarker>(
-        name,
-        |stack, device| {
-            async move {
-                let (error, id) = stack.add_ethernet_interface(name, device).await
-                    .context("failed to add ethernet interface")?;
-                assert_eq!(error, None);
-                let interface = stack.list_interfaces().await
-                    .context("failed to list interfaces")?
-                    .into_iter()
-                    .find(|interface| interface.id == id)
-                    .ok_or(failure::err_msg("failed to find added ethernet interface"))?;
-                assert_eq!(
-                    interface.properties.features
-                        & fidl_fuchsia_hardware_ethernet::INFO_FEATURE_LOOPBACK,
-                    0
-                );
-                assert_eq!(
-                    interface.properties.physical_status,
-                    fidl_fuchsia_net_stack::PhysicalStatus::Down
-                );
-                Ok(())
-            }
-        },
-    ).await
+    with_netstack_and_device::<_, _, fidl_fuchsia_net_stack::StackMarker>(name, |stack, device| {
+        async move {
+            let (error, id) = stack
+                .add_ethernet_interface(name, device)
+                .await
+                .context("failed to add ethernet interface")?;
+            assert_eq!(error, None);
+            let interface = stack
+                .list_interfaces()
+                .await
+                .context("failed to list interfaces")?
+                .into_iter()
+                .find(|interface| interface.id == id)
+                .ok_or(failure::err_msg("failed to find added ethernet interface"))?;
+            assert_eq!(
+                interface.properties.features
+                    & fidl_fuchsia_hardware_ethernet::INFO_FEATURE_LOOPBACK,
+                0
+            );
+            assert_eq!(
+                interface.properties.physical_status,
+                fidl_fuchsia_net_stack::PhysicalStatus::Down
+            );
+            Ok(())
+        }
+    })
+    .await
 }
 
 #[fuchsia_async::run_singlethreaded(test)]
@@ -266,11 +274,13 @@
         }),
         prefix_len: 32,
     };
-    let error = stack.add_interface_address(loopback.id, &mut interface_address).await
+    let error = stack
+        .add_interface_address(loopback.id, &mut interface_address)
+        .await
         .context("failed to call add interface address")?;
     assert_eq!(error.as_ref(), None);
-    let (loopback, error) = stack.get_interface_info(loopback.id).await
-        .context("failed to get loopback interface")?;
+    let (loopback, error) =
+        stack.get_interface_info(loopback.id).await.context("failed to get loopback interface")?;
     assert_eq!(error.as_ref(), None);
     let loopback = loopback.ok_or(failure::err_msg("failed to find loopback"))?;
 
@@ -281,11 +291,13 @@
         loopback.properties.addresses
     );
 
-    let error = stack.del_interface_address(loopback.id, &mut interface_address).await
+    let error = stack
+        .del_interface_address(loopback.id, &mut interface_address)
+        .await
         .context("failed to call del interface address")?;
     assert_eq!(error.as_ref(), None);
-    let (loopback, error) = stack.get_interface_info(loopback.id).await
-        .context("failed to get loopback interface")?;
+    let (loopback, error) =
+        stack.get_interface_info(loopback.id).await.context("failed to get loopback interface")?;
     assert_eq!(error.as_ref(), None);
     let loopback = loopback.ok_or(failure::err_msg("failed to find loopback"))?;
 
@@ -321,7 +333,9 @@
     };
 
     // NET-2234 (crash on interface not found).
-    let error = stack.add_interface_address(max_id + 1, &mut interface_address).await
+    let error = stack
+        .add_interface_address(max_id + 1, &mut interface_address)
+        .await
         .context("failed to call add interface address")?
         .ok_or(failure::err_msg("failed to get add interface address error"))?;
     assert_eq!(
@@ -331,12 +345,14 @@
 
     // NET-2334 (crash on invalid prefix length).
     interface_address.prefix_len = 43;
-    let error = stack.add_interface_address(max_id, &mut interface_address).await
+    let error = stack
+        .add_interface_address(max_id, &mut interface_address)
+        .await
         .context("failed to call add interface address")?
         .ok_or(failure::err_msg("failed to get add interface address error"))?;
     assert_eq!(
         error.as_ref(),
-        &fidl_fuchsia_net_stack::Error { type_: fidl_fuchsia_net_stack::ErrorType::BadState }
+        &fidl_fuchsia_net_stack::Error { type_: fidl_fuchsia_net_stack::ErrorType::InvalidArgs }
     );
 
     Ok(())
@@ -356,8 +372,8 @@
         .context("failed to connect to netstack")?;
     let interfaces = stack.list_interfaces().await.context("failed to list interfaces")?;
     let max_id = interfaces.iter().map(|interface| interface.id).max().unwrap_or(0);
-    let (info, error) = stack.get_interface_info(max_id + 1).await
-        .context("failed to call get interface info")?;
+    let (info, error) =
+        stack.get_interface_info(max_id + 1).await.context("failed to call get interface info")?;
     assert_eq!(info, None);
     let error = error.ok_or(failure::err_msg("failed to get get interface info error"))?;
     assert_eq!(
@@ -422,32 +438,38 @@
     let server_environment = create_netstack_environment(&sandbox, format!("{}_server", name))
         .context("failed to create server environment")?;
     let server_endpoint_name = "server";
-    let server_endpoint = create_endpoint(server_endpoint_name, &endpoint_manager).await
+    let server_endpoint = create_endpoint(server_endpoint_name, &endpoint_manager)
+        .await
         .context("failed to create endpoint")?;
-    let () =
-        server_endpoint.set_link_up(true).await.context("failed to start server endpoint")?;
+    let () = server_endpoint.set_link_up(true).await.context("failed to start server endpoint")?;
     {
-        let server_device = server_endpoint.get_ethernet_device().await
+        let server_device = server_endpoint
+            .get_ethernet_device()
+            .await
             .context("failed to get server ethernet device")?;
         let server_stack =
             connect_to_service::<fidl_fuchsia_net_stack::StackMarker>(&server_environment)
                 .context("failed to connect to server stack")?;
-        let (error, id) = server_stack.add_ethernet_interface(name, server_device).await
+        let (error, id) = server_stack
+            .add_ethernet_interface(name, server_device)
+            .await
             .context("failed to add server ethernet interface")?;
         assert_eq!(error, None);
-        let error = server_stack.add_interface_address(
-            id,
-            &mut fidl_fuchsia_net_stack::InterfaceAddress {
-                ip_address: fidl_fuchsia_net::IpAddress::Ipv4(fidl_fuchsia_net::Ipv4Address {
-                    addr: [192, 168, 0, 1],
-                }),
-                prefix_len: 24,
-            },
-        ).await
+        let error = server_stack
+            .add_interface_address(
+                id,
+                &mut fidl_fuchsia_net_stack::InterfaceAddress {
+                    ip_address: fidl_fuchsia_net::IpAddress::Ipv4(fidl_fuchsia_net::Ipv4Address {
+                        addr: [192, 168, 0, 1],
+                    }),
+                    prefix_len: 24,
+                },
+            )
+            .await
             .context("failed to add interface address")?;
         assert_eq!(error, None);
-        let error = server_stack.enable_interface(id).await
-            .context("failed to enable server interface")?;
+        let error =
+            server_stack.enable_interface(id).await.context("failed to enable server interface")?;
         assert_eq!(error, None);
     }
     let launcher = {
@@ -465,10 +487,10 @@
     let client_environment = create_netstack_environment(&sandbox, format!("{}_client", name))
         .context("failed to create client environment")?;
     let client_endpoint_name = "client";
-    let client_endpoint = create_endpoint(client_endpoint_name, &endpoint_manager).await
+    let client_endpoint = create_endpoint(client_endpoint_name, &endpoint_manager)
+        .await
         .context("failed to create endpoint")?;
-    let () =
-        client_endpoint.set_link_up(true).await.context("failed to start client endpoint")?;
+    let () = client_endpoint.set_link_up(true).await.context("failed to start client endpoint")?;
 
     let network_manager = {
         let (client, server) =
@@ -479,43 +501,55 @@
         client
     };
 
-    let (status, network) = network_manager.create_network(
-        name,
-        fidl_fuchsia_netemul_network::NetworkConfig {
-            latency: None,
-            packet_loss: None,
-            reorder: None,
-        },
-    ).await
+    let (status, network) = network_manager
+        .create_network(
+            name,
+            fidl_fuchsia_netemul_network::NetworkConfig {
+                latency: None,
+                packet_loss: None,
+                reorder: None,
+            },
+        )
+        .await
         .context("failed to create network")?;
     let network = network
         .ok_or(failure::err_msg("failed to create network"))?
         .into_proxy()
         .context("failed to get network proxy")?;
     let () = fuchsia_zircon::Status::ok(status).context("failed to create network")?;
-    let status = network.attach_endpoint(server_endpoint_name).await
+    let status = network
+        .attach_endpoint(server_endpoint_name)
+        .await
         .context("failed to attach server endpoint")?;
     let () = fuchsia_zircon::Status::ok(status).context("failed to attach server endpoint")?;
-    let status = network.attach_endpoint(client_endpoint_name).await
+    let status = network
+        .attach_endpoint(client_endpoint_name)
+        .await
         .context("failed to attach client endpoint")?;
     let () = fuchsia_zircon::Status::ok(status).context("failed to attach client endpoint")?;
 
     {
-        let client_device = client_endpoint.get_ethernet_device().await
+        let client_device = client_endpoint
+            .get_ethernet_device()
+            .await
             .context("failed to get client ethernet device")?;
         let client_stack =
             connect_to_service::<fidl_fuchsia_net_stack::StackMarker>(&client_environment)
                 .context("failed to connect to client stack")?;
-        let (error, id) = client_stack.add_ethernet_interface(name, client_device).await
+        let (error, id) = client_stack
+            .add_ethernet_interface(name, client_device)
+            .await
             .context("failed to add client ethernet interface")?;
         assert_eq!(error, None);
-        let error = client_stack.enable_interface(id).await
-            .context("failed to enable client interface")?;
+        let error =
+            client_stack.enable_interface(id).await.context("failed to enable client interface")?;
         assert_eq!(error, None);
         let client_netstack =
             connect_to_service::<fidl_fuchsia_netstack::NetstackMarker>(&client_environment)
                 .context("failed to connect to client netstack")?;
-        let error = client_netstack.set_dhcp_client_status(id as u32, true).await
+        let error = client_netstack
+            .set_dhcp_client_status(id as u32, true)
+            .await
             .context("failed to set DHCP client status")?;
         assert_eq!(error.status, fidl_fuchsia_netstack::Status::Ok, "{}", error.message);
 
@@ -552,7 +586,8 @@
             fuchsia_async::Time::after(fuchsia_zircon::Duration::from_seconds(60)),
             || None,
         );
-        let (addr, netmask) = address_change.await
+        let (addr, netmask) = address_change
+            .await
             .ok_or(failure::err_msg("failed to observe DHCP acquisition"))?
             .context("failed to observe DHCP acquisition")?;
         assert_eq!(