diff --git a/test/benchmarks/tcp/BUILD b/test/benchmarks/tcp/BUILD index 6dde7d9e6..2102c78ab 100644 --- a/test/benchmarks/tcp/BUILD +++ b/test/benchmarks/tcp/BUILD @@ -13,7 +13,9 @@ go_binary( "//pkg/tcpip/link/qdisc/fifo", "//pkg/tcpip/network/arp", "//pkg/tcpip/network/ipv4", + "//pkg/tcpip/network/ipv6", "//pkg/tcpip/stack", + "//pkg/tcpip/transport/icmp", "//pkg/tcpip/transport/tcp", "//pkg/tcpip/transport/udp", "@org_golang_x_sys//unix:go_default_library", diff --git a/test/benchmarks/tcp/tcp_benchmark.sh b/test/benchmarks/tcp/tcp_benchmark.sh index 2b009e3b6..dfadd6aa6 100755 --- a/test/benchmarks/tcp/tcp_benchmark.sh +++ b/test/benchmarks/tcp/tcp_benchmark.sh @@ -19,11 +19,15 @@ # Fixed parameters. iperf_port=45201 # Not likely to be privileged. proxy_port=44000 # Ditto. +mask=8 + client_addr=10.0.0.1 client_proxy_addr=10.0.0.2 server_proxy_addr=10.0.0.3 server_addr=10.0.0.4 -mask=8 +full_server_addr=${server_addr}:${iperf_port} +full_server_proxy_addr=${server_proxy_addr}:${proxy_port} +iperf_version_arg= # Defaults; this provides a reasonable approximation of a decent internet link. # Parameters can be varied independently from this set to see response to @@ -140,6 +144,16 @@ while [ $# -gt 0 ]; do --disable-linux-gro) disable_linux_gro=1 ;; + --ipv6) + client_addr=fd::1 + client_proxy_addr=fd::2 + server_proxy_addr=fd::3 + server_addr=fd::4 + full_server_addr=[${server_addr}]:${iperf_port} + full_server_proxy_addr=[${server_proxy_addr}]:${proxy_port} + iperf_version_arg=-V + netstack_opts="${netstack_opts} -ipv6" + ;; --num-client-threads) shift num_client_threads=$1 @@ -171,6 +185,7 @@ while [ $# -gt 0 ]; do echo " --num-client-threads number of parallel client threads to run" echo " --disable-linux-gso disable segmentation offload (TSO, GSO, GRO) in the Linux network stack" echo " --disable-linux-gro disable GRO in the Linux network stack" + echo " --ipv6 use ipv6 for benchmarks" echo "" echo "The output will of the script will be:" echo " " @@ -213,24 +228,24 @@ fi # Client proxy that will listen on the client's iperf target forward traffic # using the host networking stack. -client_args="${proxy_binary} -port ${proxy_port} -forward ${server_proxy_addr}:${proxy_port}" +client_args="${proxy_binary} -port ${proxy_port} -forward ${full_server_proxy_addr}" if ${client}; then # Client proxy that will listen on the client's iperf target # and forward traffic using netstack. client_args="${proxy_binary} ${netstack_opts} -port ${proxy_port} -client \\ -mtu ${mtu} -iface client.0 -addr ${client_proxy_addr} -mask ${mask} \\ - -forward ${server_proxy_addr}:${proxy_port} -gso=${gso} -swgso=${swgso}" + -forward ${full_server_proxy_addr} -gso=${gso} -swgso=${swgso}" fi # Server proxy that will listen on the proxy port and forward to the server's # iperf server using the host networking stack. -server_args="${proxy_binary} -port ${proxy_port} -forward ${server_addr}:${iperf_port}" +server_args="${proxy_binary} -port ${proxy_port} -forward ${full_server_addr}" if ${server}; then # Server proxy that will listen on the proxy port and forward to the servers' # iperf server using netstack. server_args="${proxy_binary} ${netstack_opts} -port ${proxy_port} -server \\ -mtu ${mtu} -iface server.0 -addr ${server_proxy_addr} -mask ${mask} \\ - -forward ${server_addr}:${iperf_port} -gso=${gso} -swgso=${swgso}" + -forward ${full_server_addr} -gso=${gso} -swgso=${swgso}" fi # Specify loss and duplicate parameters only if they are non-zero @@ -344,15 +359,19 @@ ${nsjoin_binary} /tmp/server.netns ip link set lo up ip link set dev client.1 up ip link set dev server.1 up -${nsjoin_binary} /tmp/client.netns ${client_args} & -client_pid=\$! ${nsjoin_binary} /tmp/server.netns ${server_args} & server_pid=\$! # Start the iperf server. -${nsjoin_binary} /tmp/server.netns iperf -p ${iperf_port} -s >&2 & +${nsjoin_binary} /tmp/server.netns iperf ${iperf_version_arg} -p ${iperf_port} -s >&2 & iperf_pid=\$! +# Give services time to start. +sleep 5 + +${nsjoin_binary} /tmp/client.netns ${client_args} & +client_pid=\$! + # Show traffic information. if ! ${client} && ! ${server}; then ${nsjoin_binary} /tmp/client.netns ping -c 100 -i 0.001 -W 1 ${server_addr} >&2 || true @@ -374,7 +393,7 @@ trap cleanup EXIT # Run the benchmark, recording the results file. while ${nsjoin_binary} /tmp/client.netns iperf \\ - -p ${proxy_port} -c ${client_addr} -t ${duration} -f m -P ${num_client_threads} 2>&1 \\ + ${iperf_version_arg} -p ${proxy_port} -c ${client_addr} -t ${duration} -f m -P ${num_client_threads} 2>&1 \\ | tee \$results_file \\ | grep "connect failed" >/dev/null; do sleep 0.1 # Wait for all services. diff --git a/test/benchmarks/tcp/tcp_proxy.go b/test/benchmarks/tcp/tcp_proxy.go index 3d5fa9961..96ecc0b57 100644 --- a/test/benchmarks/tcp/tcp_proxy.go +++ b/test/benchmarks/tcp/tcp_proxy.go @@ -38,7 +38,9 @@ import ( "gvisor.dev/gvisor/pkg/tcpip/link/qdisc/fifo" "gvisor.dev/gvisor/pkg/tcpip/network/arp" "gvisor.dev/gvisor/pkg/tcpip/network/ipv4" + "gvisor.dev/gvisor/pkg/tcpip/network/ipv6" "gvisor.dev/gvisor/pkg/tcpip/stack" + "gvisor.dev/gvisor/pkg/tcpip/transport/icmp" "gvisor.dev/gvisor/pkg/tcpip/transport/tcp" "gvisor.dev/gvisor/pkg/tcpip/transport/udp" ) @@ -64,6 +66,7 @@ var ( serverTCPProbeFile = flag.String("server_tcp_probe_file", "", "if specified, installs a tcp probe to dump endpoint state to the specified file.") cpuprofile = flag.String("cpuprofile", "", "write cpu profile to the specified file.") memprofile = flag.String("memprofile", "", "write memory profile to the specified file.") + useIpv6 = flag.Bool("ipv6", false, "use ipv6 instead of ipv4.") ) type impl interface { @@ -156,26 +159,37 @@ func newNetstackImpl(mode string) (impl, error) { // Parse details. parsedAddr := tcpip.Address(net.ParseIP(*addr).To4()) - parsedDest := tcpip.Address("") // Filled in below. - parsedMask := tcpip.AddressMask("") // Filled in below. + if *useIpv6 { + parsedAddr = tcpip.Address(net.ParseIP(*addr).To16()) + } + parsedDest := tcpip.Address("") // Filled in below. + parsedMask := tcpip.AddressMask("") // Filled in below. + parsedDest6 := tcpip.Address("") // Filled in below. + parsedMask6 := tcpip.AddressMask("") // Filled in below. switch *mask { case 8: parsedDest = tcpip.Address([]byte{parsedAddr[0], 0, 0, 0}) parsedMask = tcpip.AddressMask([]byte{0xff, 0, 0, 0}) + parsedDest6 = tcpip.Address(append([]byte{parsedAddr[0]}, make([]byte, 15)...)) + parsedMask6 = tcpip.AddressMask(append([]byte{0xff}, make([]byte, 15)...)) case 16: parsedDest = tcpip.Address([]byte{parsedAddr[0], parsedAddr[1], 0, 0}) parsedMask = tcpip.AddressMask([]byte{0xff, 0xff, 0, 0}) + parsedDest6 = tcpip.Address(append([]byte{parsedAddr[0], parsedAddr[1]}, make([]byte, 14)...)) + parsedMask6 = tcpip.AddressMask(append([]byte{0xff, 0xff}, make([]byte, 14)...)) case 24: parsedDest = tcpip.Address([]byte{parsedAddr[0], parsedAddr[1], parsedAddr[2], 0}) parsedMask = tcpip.AddressMask([]byte{0xff, 0xff, 0xff, 0}) + parsedDest6 = tcpip.Address(append([]byte{parsedAddr[0], parsedAddr[1], parsedAddr[2]}, make([]byte, 13)...)) + parsedMask6 = tcpip.AddressMask(append([]byte{0xff, 0xff, 0xff}, make([]byte, 13)...)) default: // This is just laziness; we don't expect a different mask. return nil, fmt.Errorf("mask %d not supported", mask) } // Create a new network stack. - netProtos := []stack.NetworkProtocolFactory{ipv4.NewProtocol, arp.NewProtocol} - transProtos := []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol} + netProtos := []stack.NetworkProtocolFactory{ipv6.NewProtocol, ipv4.NewProtocol, arp.NewProtocol} + transProtos := []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol, icmp.NewProtocol4, icmp.NewProtocol6} s := stack.New(stack.Options{ NetworkProtocols: netProtos, TransportProtocols: transProtos, @@ -210,22 +224,35 @@ func newNetstackImpl(mode string) (impl, error) { if err := s.CreateNICWithOptions(nicID, ep, opts); err != nil { return nil, fmt.Errorf("error creating NIC %q: %v", *iface, err) } + proto := ipv4.ProtocolNumber + if *useIpv6 { + proto = ipv6.ProtocolNumber + } protocolAddr := tcpip.ProtocolAddress{ - Protocol: ipv4.ProtocolNumber, + Protocol: proto, AddressWithPrefix: parsedAddr.WithPrefix(), } if err := s.AddProtocolAddress(nicID, protocolAddr, stack.AddressProperties{}); err != nil { return nil, fmt.Errorf("error adding IP address %+v to %q: %s", protocolAddr, *iface, err) } - subnet, err := tcpip.NewSubnet(parsedDest, parsedMask) + subnet4, err := tcpip.NewSubnet(parsedDest, parsedMask) if err != nil { return nil, fmt.Errorf("tcpip.Subnet(%s, %s): %s", parsedDest, parsedMask, err) } + subnet6, err := tcpip.NewSubnet(parsedDest6, parsedMask6) + if err != nil { + return nil, fmt.Errorf("tcpip.Subnet(%s, %s): %s", parsedDest, parsedMask, err) + } + // Add default route; we only support s.SetRouteTable([]tcpip.Route{ { - Destination: subnet, + Destination: subnet4, + NIC: nicID, + }, + { + Destination: subnet6, NIC: nicID, }, }) @@ -281,12 +308,20 @@ func (n netstackImpl) dial(address string) (net.Conn, error) { if err != nil { return nil, err } + hostAddr := net.ParseIP(host).To4() + if *useIpv6 { + hostAddr = net.ParseIP(host).To16() + } addr := tcpip.FullAddress{ NIC: nicID, - Addr: tcpip.Address(net.ParseIP(host).To4()), + Addr: tcpip.Address(hostAddr), Port: uint16(portNumber), } - conn, err := gonet.DialTCP(n.s, addr, ipv4.ProtocolNumber) + proto := ipv4.ProtocolNumber + if *useIpv6 { + proto = ipv6.ProtocolNumber + } + conn, err := gonet.DialTCP(n.s, addr, proto) if err != nil { return nil, err } @@ -298,7 +333,11 @@ func (n netstackImpl) listen(port int) (net.Listener, error) { NIC: nicID, Port: uint16(port), } - listener, err := gonet.ListenTCP(n.s, addr, ipv4.ProtocolNumber) + proto := ipv4.ProtocolNumber + if *useIpv6 { + proto = ipv6.ProtocolNumber + } + listener, err := gonet.ListenTCP(n.s, addr, proto) if err != nil { return nil, err }