diff options
Diffstat (limited to 'test/packetimpact/testbench')
-rw-r--r-- | test/packetimpact/testbench/BUILD | 31 | ||||
-rw-r--r-- | test/packetimpact/testbench/connections.go | 245 | ||||
-rw-r--r-- | test/packetimpact/testbench/dut.go | 363 | ||||
-rw-r--r-- | test/packetimpact/testbench/dut_client.go | 28 | ||||
-rw-r--r-- | test/packetimpact/testbench/layers.go | 507 | ||||
-rw-r--r-- | test/packetimpact/testbench/rawsockets.go | 151 |
6 files changed, 0 insertions, 1325 deletions
diff --git a/test/packetimpact/testbench/BUILD b/test/packetimpact/testbench/BUILD deleted file mode 100644 index a34c81fcc..000000000 --- a/test/packetimpact/testbench/BUILD +++ /dev/null @@ -1,31 +0,0 @@ -load("//tools:defs.bzl", "go_library") - -package( - default_visibility = ["//test/packetimpact:__subpackages__"], - licenses = ["notice"], -) - -go_library( - name = "testbench", - srcs = [ - "connections.go", - "dut.go", - "dut_client.go", - "layers.go", - "rawsockets.go", - ], - deps = [ - "//pkg/tcpip", - "//pkg/tcpip/header", - "//pkg/tcpip/seqnum", - "//pkg/usermem", - "//test/packetimpact/proto:posix_server_go_proto", - "@com_github_google_go-cmp//cmp:go_default_library", - "@com_github_google_go-cmp//cmp/cmpopts:go_default_library", - "@com_github_imdario_mergo//:go_default_library", - "@com_github_mohae_deepcopy//:go_default_library", - "@org_golang_google_grpc//:go_default_library", - "@org_golang_google_grpc//keepalive:go_default_library", - "@org_golang_x_sys//unix:go_default_library", - ], -) diff --git a/test/packetimpact/testbench/connections.go b/test/packetimpact/testbench/connections.go deleted file mode 100644 index b7aa63934..000000000 --- a/test/packetimpact/testbench/connections.go +++ /dev/null @@ -1,245 +0,0 @@ -// Copyright 2020 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package testbench has utilities to send and receive packets and also command -// the DUT to run POSIX functions. -package testbench - -import ( - "flag" - "fmt" - "math/rand" - "net" - "testing" - "time" - - "github.com/mohae/deepcopy" - "golang.org/x/sys/unix" - "gvisor.dev/gvisor/pkg/tcpip" - "gvisor.dev/gvisor/pkg/tcpip/header" - "gvisor.dev/gvisor/pkg/tcpip/seqnum" -) - -var localIPv4 = flag.String("local_ipv4", "", "local IPv4 address for test packets") -var remoteIPv4 = flag.String("remote_ipv4", "", "remote IPv4 address for test packets") -var localMAC = flag.String("local_mac", "", "local mac address for test packets") -var remoteMAC = flag.String("remote_mac", "", "remote mac address for test packets") - -// TCPIPv4 maintains state about a TCP/IPv4 connection. -type TCPIPv4 struct { - outgoing Layers - incoming Layers - LocalSeqNum seqnum.Value - RemoteSeqNum seqnum.Value - SynAck *TCP - sniffer Sniffer - injector Injector - portPickerFD int - t *testing.T -} - -// pickPort makes a new socket and returns the socket FD and port. The caller -// must close the FD when done with the port if there is no error. -func pickPort() (int, uint16, error) { - fd, err := unix.Socket(unix.AF_INET, unix.SOCK_STREAM, 0) - if err != nil { - return -1, 0, err - } - var sa unix.SockaddrInet4 - copy(sa.Addr[0:4], net.ParseIP(*localIPv4).To4()) - if err := unix.Bind(fd, &sa); err != nil { - unix.Close(fd) - return -1, 0, err - } - newSockAddr, err := unix.Getsockname(fd) - if err != nil { - unix.Close(fd) - return -1, 0, err - } - newSockAddrInet4, ok := newSockAddr.(*unix.SockaddrInet4) - if !ok { - unix.Close(fd) - return -1, 0, fmt.Errorf("can't cast Getsockname result to SockaddrInet4") - } - return fd, uint16(newSockAddrInet4.Port), nil -} - -// tcpLayerIndex is the position of the TCP layer in the TCPIPv4 connection. It -// is the third, after Ethernet and IPv4. -const tcpLayerIndex int = 2 - -// NewTCPIPv4 creates a new TCPIPv4 connection with reasonable defaults. -func NewTCPIPv4(t *testing.T, dut DUT, outgoingTCP, incomingTCP TCP) TCPIPv4 { - lMAC, err := tcpip.ParseMACAddress(*localMAC) - if err != nil { - t.Fatalf("can't parse localMAC %q: %s", *localMAC, err) - } - - rMAC, err := tcpip.ParseMACAddress(*remoteMAC) - if err != nil { - t.Fatalf("can't parse remoteMAC %q: %s", *remoteMAC, err) - } - - portPickerFD, localPort, err := pickPort() - if err != nil { - t.Fatalf("can't pick a port: %s", err) - } - lIP := tcpip.Address(net.ParseIP(*localIPv4).To4()) - rIP := tcpip.Address(net.ParseIP(*remoteIPv4).To4()) - - sniffer, err := NewSniffer(t) - if err != nil { - t.Fatalf("can't make new sniffer: %s", err) - } - - injector, err := NewInjector(t) - if err != nil { - t.Fatalf("can't make new injector: %s", err) - } - - newOutgoingTCP := &TCP{ - DataOffset: Uint8(header.TCPMinimumSize), - WindowSize: Uint16(32768), - SrcPort: &localPort, - } - if err := newOutgoingTCP.merge(outgoingTCP); err != nil { - t.Fatalf("can't merge %v into %v: %s", outgoingTCP, newOutgoingTCP, err) - } - newIncomingTCP := &TCP{ - DstPort: &localPort, - } - if err := newIncomingTCP.merge(incomingTCP); err != nil { - t.Fatalf("can't merge %v into %v: %s", incomingTCP, newIncomingTCP, err) - } - return TCPIPv4{ - outgoing: Layers{ - &Ether{SrcAddr: &lMAC, DstAddr: &rMAC}, - &IPv4{SrcAddr: &lIP, DstAddr: &rIP}, - newOutgoingTCP}, - incoming: Layers{ - &Ether{SrcAddr: &rMAC, DstAddr: &lMAC}, - &IPv4{SrcAddr: &rIP, DstAddr: &lIP}, - newIncomingTCP}, - sniffer: sniffer, - injector: injector, - portPickerFD: portPickerFD, - t: t, - LocalSeqNum: seqnum.Value(rand.Uint32()), - } -} - -// Close the injector and sniffer associated with this connection. -func (conn *TCPIPv4) Close() { - conn.sniffer.Close() - conn.injector.Close() - if err := unix.Close(conn.portPickerFD); err != nil { - conn.t.Fatalf("can't close portPickerFD: %s", err) - } - conn.portPickerFD = -1 -} - -// Send a packet with reasonable defaults and override some fields by tcp. -func (conn *TCPIPv4) Send(tcp TCP, additionalLayers ...Layer) { - if tcp.SeqNum == nil { - tcp.SeqNum = Uint32(uint32(conn.LocalSeqNum)) - } - if tcp.AckNum == nil { - tcp.AckNum = Uint32(uint32(conn.RemoteSeqNum)) - } - layersToSend := deepcopy.Copy(conn.outgoing).(Layers) - if err := layersToSend[tcpLayerIndex].(*TCP).merge(tcp); err != nil { - conn.t.Fatalf("can't merge %v into %v: %s", tcp, layersToSend[tcpLayerIndex], err) - } - layersToSend = append(layersToSend, additionalLayers...) - outBytes, err := layersToSend.toBytes() - if err != nil { - conn.t.Fatalf("can't build outgoing TCP packet: %s", err) - } - conn.injector.Send(outBytes) - - // Compute the next TCP sequence number. - for i := tcpLayerIndex + 1; i < len(layersToSend); i++ { - conn.LocalSeqNum.UpdateForward(seqnum.Size(layersToSend[i].length())) - } - if tcp.Flags != nil && *tcp.Flags&(header.TCPFlagSyn|header.TCPFlagFin) != 0 { - conn.LocalSeqNum.UpdateForward(1) - } -} - -// Recv gets a packet from the sniffer within the timeout provided. If no packet -// arrives before the timeout, it returns nil. -func (conn *TCPIPv4) Recv(timeout time.Duration) *TCP { - deadline := time.Now().Add(timeout) - for { - timeout = deadline.Sub(time.Now()) - if timeout <= 0 { - break - } - b := conn.sniffer.Recv(timeout) - if b == nil { - break - } - layers, err := ParseEther(b) - if err != nil { - continue // Ignore packets that can't be parsed. - } - if !conn.incoming.match(layers) { - continue // Ignore packets that don't match the expected incoming. - } - tcpHeader := (layers[tcpLayerIndex]).(*TCP) - conn.RemoteSeqNum = seqnum.Value(*tcpHeader.SeqNum) - if *tcpHeader.Flags&(header.TCPFlagSyn|header.TCPFlagFin) != 0 { - conn.RemoteSeqNum.UpdateForward(1) - } - for i := tcpLayerIndex + 1; i < len(layers); i++ { - conn.RemoteSeqNum.UpdateForward(seqnum.Size(layers[i].length())) - } - return tcpHeader - } - return nil -} - -// Expect a packet that matches the provided tcp within the timeout specified. -// If it doesn't arrive in time, the test fails. -func (conn *TCPIPv4) Expect(tcp TCP, timeout time.Duration) *TCP { - deadline := time.Now().Add(timeout) - for { - timeout = deadline.Sub(time.Now()) - if timeout <= 0 { - return nil - } - gotTCP := conn.Recv(timeout) - if gotTCP == nil { - return nil - } - if tcp.match(gotTCP) { - return gotTCP - } - } -} - -// Handshake performs a TCP 3-way handshake. -func (conn *TCPIPv4) Handshake() { - // Send the SYN. - conn.Send(TCP{Flags: Uint8(header.TCPFlagSyn)}) - - // Wait for the SYN-ACK. - conn.SynAck = conn.Expect(TCP{Flags: Uint8(header.TCPFlagSyn | header.TCPFlagAck)}, time.Second) - if conn.SynAck == nil { - conn.t.Fatalf("didn't get synack during handshake") - } - - // Send an ACK. - conn.Send(TCP{Flags: Uint8(header.TCPFlagAck)}) -} diff --git a/test/packetimpact/testbench/dut.go b/test/packetimpact/testbench/dut.go deleted file mode 100644 index 8ea1706d3..000000000 --- a/test/packetimpact/testbench/dut.go +++ /dev/null @@ -1,363 +0,0 @@ -// Copyright 2020 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package testbench - -import ( - "context" - "flag" - "net" - "strconv" - "syscall" - "testing" - "time" - - pb "gvisor.dev/gvisor/test/packetimpact/proto/posix_server_go_proto" - - "golang.org/x/sys/unix" - "google.golang.org/grpc" - "google.golang.org/grpc/keepalive" -) - -var ( - posixServerIP = flag.String("posix_server_ip", "", "ip address to listen to for UDP commands") - posixServerPort = flag.Int("posix_server_port", 40000, "port to listen to for UDP commands") - rpcTimeout = flag.Duration("rpc_timeout", 100*time.Millisecond, "gRPC timeout") - rpcKeepalive = flag.Duration("rpc_keepalive", 10*time.Second, "gRPC keepalive") -) - -// DUT communicates with the DUT to force it to make POSIX calls. -type DUT struct { - t *testing.T - conn *grpc.ClientConn - posixServer PosixClient -} - -// NewDUT creates a new connection with the DUT over gRPC. -func NewDUT(t *testing.T) DUT { - flag.Parse() - posixServerAddress := *posixServerIP + ":" + strconv.Itoa(*posixServerPort) - conn, err := grpc.Dial(posixServerAddress, grpc.WithInsecure(), grpc.WithKeepaliveParams(keepalive.ClientParameters{Timeout: *rpcKeepalive})) - if err != nil { - t.Fatalf("failed to grpc.Dial(%s): %s", posixServerAddress, err) - } - posixServer := NewPosixClient(conn) - return DUT{ - t: t, - conn: conn, - posixServer: posixServer, - } -} - -// TearDown closes the underlying connection. -func (dut *DUT) TearDown() { - dut.conn.Close() -} - -// SocketWithErrno calls socket on the DUT and returns the fd and errno. -func (dut *DUT) SocketWithErrno(domain, typ, proto int32) (int32, error) { - dut.t.Helper() - req := pb.SocketRequest{ - Domain: domain, - Type: typ, - Protocol: proto, - } - ctx := context.Background() - resp, err := dut.posixServer.Socket(ctx, &req) - if err != nil { - dut.t.Fatalf("failed to call Socket: %s", err) - } - return resp.GetFd(), syscall.Errno(resp.GetErrno_()) -} - -// Socket calls socket on the DUT and returns the file descriptor. If socket -// fails on the DUT, the test ends. -func (dut *DUT) Socket(domain, typ, proto int32) int32 { - dut.t.Helper() - fd, err := dut.SocketWithErrno(domain, typ, proto) - if fd < 0 { - dut.t.Fatalf("failed to create socket: %s", err) - } - return fd -} - -func (dut *DUT) sockaddrToProto(sa unix.Sockaddr) *pb.Sockaddr { - dut.t.Helper() - switch s := sa.(type) { - case *unix.SockaddrInet4: - return &pb.Sockaddr{ - Sockaddr: &pb.Sockaddr_In{ - In: &pb.SockaddrIn{ - Family: unix.AF_INET, - Port: uint32(s.Port), - Addr: s.Addr[:], - }, - }, - } - case *unix.SockaddrInet6: - return &pb.Sockaddr{ - Sockaddr: &pb.Sockaddr_In6{ - In6: &pb.SockaddrIn6{ - Family: unix.AF_INET6, - Port: uint32(s.Port), - Flowinfo: 0, - ScopeId: s.ZoneId, - Addr: s.Addr[:], - }, - }, - } - } - dut.t.Fatalf("can't parse Sockaddr: %+v", sa) - return nil -} - -func (dut *DUT) protoToSockaddr(sa *pb.Sockaddr) unix.Sockaddr { - dut.t.Helper() - switch s := sa.Sockaddr.(type) { - case *pb.Sockaddr_In: - ret := unix.SockaddrInet4{ - Port: int(s.In.GetPort()), - } - copy(ret.Addr[:], s.In.GetAddr()) - return &ret - case *pb.Sockaddr_In6: - ret := unix.SockaddrInet6{ - Port: int(s.In6.GetPort()), - ZoneId: s.In6.GetScopeId(), - } - copy(ret.Addr[:], s.In6.GetAddr()) - } - dut.t.Fatalf("can't parse Sockaddr: %+v", sa) - return nil -} - -// BindWithErrno calls bind on the DUT. -func (dut *DUT) BindWithErrno(fd int32, sa unix.Sockaddr) (int32, error) { - dut.t.Helper() - req := pb.BindRequest{ - Sockfd: fd, - Addr: dut.sockaddrToProto(sa), - } - ctx := context.Background() - resp, err := dut.posixServer.Bind(ctx, &req) - if err != nil { - dut.t.Fatalf("failed to call Bind: %s", err) - } - return resp.GetRet(), syscall.Errno(resp.GetErrno_()) -} - -// Bind calls bind on the DUT and causes a fatal test failure if it doesn't -// succeed. -func (dut *DUT) Bind(fd int32, sa unix.Sockaddr) { - dut.t.Helper() - ret, err := dut.BindWithErrno(fd, sa) - if ret != 0 { - dut.t.Fatalf("failed to bind socket: %s", err) - } -} - -// GetSockNameWithErrno calls getsockname on the DUT. -func (dut *DUT) GetSockNameWithErrno(sockfd int32) (int32, unix.Sockaddr, error) { - dut.t.Helper() - req := pb.GetSockNameRequest{ - Sockfd: sockfd, - } - ctx := context.Background() - resp, err := dut.posixServer.GetSockName(ctx, &req) - if err != nil { - dut.t.Fatalf("failed to call Bind: %s", err) - } - return resp.GetRet(), dut.protoToSockaddr(resp.GetAddr()), syscall.Errno(resp.GetErrno_()) -} - -// GetSockName calls getsockname on the DUT and causes a fatal test failure if -// it doens't succeed. -func (dut *DUT) GetSockName(sockfd int32) unix.Sockaddr { - dut.t.Helper() - ret, sa, err := dut.GetSockNameWithErrno(sockfd) - if ret != 0 { - dut.t.Fatalf("failed to getsockname: %s", err) - } - return sa -} - -// ListenWithErrno calls listen on the DUT. -func (dut *DUT) ListenWithErrno(sockfd, backlog int32) (int32, error) { - dut.t.Helper() - req := pb.ListenRequest{ - Sockfd: sockfd, - Backlog: backlog, - } - ctx, cancel := context.WithTimeout(context.Background(), *rpcTimeout) - defer cancel() - resp, err := dut.posixServer.Listen(ctx, &req) - if err != nil { - dut.t.Fatalf("failed to call Listen: %s", err) - } - return resp.GetRet(), syscall.Errno(resp.GetErrno_()) -} - -// Listen calls listen on the DUT and causes a fatal test failure if it doesn't -// succeed. -func (dut *DUT) Listen(sockfd, backlog int32) { - dut.t.Helper() - ret, err := dut.ListenWithErrno(sockfd, backlog) - if ret != 0 { - dut.t.Fatalf("failed to listen: %s", err) - } -} - -// AcceptWithErrno calls accept on the DUT. -func (dut *DUT) AcceptWithErrno(sockfd int32) (int32, unix.Sockaddr, error) { - dut.t.Helper() - req := pb.AcceptRequest{ - Sockfd: sockfd, - } - ctx, cancel := context.WithTimeout(context.Background(), *rpcTimeout) - defer cancel() - resp, err := dut.posixServer.Accept(ctx, &req) - if err != nil { - dut.t.Fatalf("failed to call Accept: %s", err) - } - return resp.GetFd(), dut.protoToSockaddr(resp.GetAddr()), syscall.Errno(resp.GetErrno_()) -} - -// Accept calls accept on the DUT and causes a fatal test failure if it doesn't -// succeed. -func (dut *DUT) Accept(sockfd int32) (int32, unix.Sockaddr) { - dut.t.Helper() - fd, sa, err := dut.AcceptWithErrno(sockfd) - if fd < 0 { - dut.t.Fatalf("failed to accept: %s", err) - } - return fd, sa -} - -// SetSockOptWithErrno calls setsockopt on the DUT. -func (dut *DUT) SetSockOptWithErrno(sockfd, level, optname int32, optval []byte) (int32, error) { - dut.t.Helper() - req := pb.SetSockOptRequest{ - Sockfd: sockfd, - Level: level, - Optname: optname, - Optval: optval, - } - ctx, cancel := context.WithTimeout(context.Background(), *rpcTimeout) - defer cancel() - resp, err := dut.posixServer.SetSockOpt(ctx, &req) - if err != nil { - dut.t.Fatalf("failed to call SetSockOpt: %s", err) - } - return resp.GetRet(), syscall.Errno(resp.GetErrno_()) -} - -// SetSockOpt calls setsockopt on the DUT and causes a fatal test failure if it -// doesn't succeed. -func (dut *DUT) SetSockOpt(sockfd, level, optname int32, optval []byte) { - dut.t.Helper() - ret, err := dut.SetSockOptWithErrno(sockfd, level, optname, optval) - if ret != 0 { - dut.t.Fatalf("failed to SetSockOpt: %s", err) - } -} - -// SetSockOptTimevalWithErrno calls setsockopt with the timeval converted to -// bytes. -func (dut *DUT) SetSockOptTimevalWithErrno(sockfd, level, optname int32, tv *unix.Timeval) (int32, error) { - dut.t.Helper() - timeval := pb.Timeval{ - Seconds: int64(tv.Sec), - Microseconds: int64(tv.Usec), - } - req := pb.SetSockOptTimevalRequest{ - Sockfd: sockfd, - Level: level, - Optname: optname, - Timeval: &timeval, - } - ctx, cancel := context.WithTimeout(context.Background(), *rpcTimeout) - defer cancel() - resp, err := dut.posixServer.SetSockOptTimeval(ctx, &req) - if err != nil { - dut.t.Fatalf("failed to call SetSockOptTimeval: %s", err) - } - return resp.GetRet(), syscall.Errno(resp.GetErrno_()) -} - -// SetSockOptTimeval calls setsockopt on the DUT and causes a fatal test failure -// if it doesn't succeed. -func (dut *DUT) SetSockOptTimeval(sockfd, level, optname int32, tv *unix.Timeval) { - dut.t.Helper() - ret, err := dut.SetSockOptTimevalWithErrno(sockfd, level, optname, tv) - if ret != 0 { - dut.t.Fatalf("failed to SetSockOptTimeval: %s", err) - } -} - -// CloseWithErrno calls close on the DUT. -func (dut *DUT) CloseWithErrno(fd int32) (int32, error) { - dut.t.Helper() - req := pb.CloseRequest{ - Fd: fd, - } - ctx, cancel := context.WithTimeout(context.Background(), *rpcTimeout) - defer cancel() - resp, err := dut.posixServer.Close(ctx, &req) - if err != nil { - dut.t.Fatalf("failed to call Close: %s", err) - } - return resp.GetRet(), syscall.Errno(resp.GetErrno_()) -} - -// Close calls close on the DUT and causes a fatal test failure if it doesn't -// succeed. -func (dut *DUT) Close(fd int32) { - dut.t.Helper() - ret, err := dut.CloseWithErrno(fd) - if ret != 0 { - dut.t.Fatalf("failed to close: %s", err) - } -} - -// CreateListener makes a new TCP connection. If it fails, the test ends. -func (dut *DUT) CreateListener(typ, proto, backlog int32) (int32, uint16) { - dut.t.Helper() - addr := net.ParseIP(*remoteIPv4) - var fd int32 - if addr.To4() != nil { - fd = dut.Socket(unix.AF_INET, typ, proto) - sa := unix.SockaddrInet4{} - copy(sa.Addr[:], addr.To4()) - dut.Bind(fd, &sa) - } else if addr.To16() != nil { - fd = dut.Socket(unix.AF_INET6, typ, proto) - sa := unix.SockaddrInet6{} - copy(sa.Addr[:], addr.To16()) - dut.Bind(fd, &sa) - } else { - dut.t.Fatal("unknown ip addr type for remoteIP") - } - sa := dut.GetSockName(fd) - var port int - switch s := sa.(type) { - case *unix.SockaddrInet4: - port = s.Port - case *unix.SockaddrInet6: - port = s.Port - default: - dut.t.Fatalf("unknown sockaddr type from getsockname: %t", sa) - } - dut.Listen(fd, backlog) - return fd, uint16(port) -} diff --git a/test/packetimpact/testbench/dut_client.go b/test/packetimpact/testbench/dut_client.go deleted file mode 100644 index b130a33a2..000000000 --- a/test/packetimpact/testbench/dut_client.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2020 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package testbench - -import ( - "google.golang.org/grpc" - pb "gvisor.dev/gvisor/test/packetimpact/proto/posix_server_go_proto" -) - -// PosixClient is a gRPC client for the Posix service. -type PosixClient pb.PosixClient - -// NewPosixClient makes a new gRPC client for the Posix service. -func NewPosixClient(c grpc.ClientConnInterface) PosixClient { - return pb.NewPosixClient(c) -} diff --git a/test/packetimpact/testbench/layers.go b/test/packetimpact/testbench/layers.go deleted file mode 100644 index 35fa4dcb6..000000000 --- a/test/packetimpact/testbench/layers.go +++ /dev/null @@ -1,507 +0,0 @@ -// Copyright 2020 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package testbench - -import ( - "fmt" - "reflect" - - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - "github.com/imdario/mergo" - "gvisor.dev/gvisor/pkg/tcpip" - "gvisor.dev/gvisor/pkg/tcpip/header" -) - -// Layer is the interface that all encapsulations must implement. -// -// A Layer is an encapsulation in a packet, such as TCP, IPv4, IPv6, etc. A -// Layer contains all the fields of the encapsulation. Each field is a pointer -// and may be nil. -type Layer interface { - // toBytes converts the Layer into bytes. In places where the Layer's field - // isn't nil, the value that is pointed to is used. When the field is nil, a - // reasonable default for the Layer is used. For example, "64" for IPv4 TTL - // and a calculated checksum for TCP or IP. Some layers require information - // from the previous or next layers in order to compute a default, such as - // TCP's checksum or Ethernet's type, so each Layer has a doubly-linked list - // to the layer's neighbors. - toBytes() ([]byte, error) - - // match checks if the current Layer matches the provided Layer. If either - // Layer has a nil in a given field, that field is considered matching. - // Otherwise, the values pointed to by the fields must match. - match(Layer) bool - - // length in bytes of the current encapsulation - length() int - - // next gets a pointer to the encapsulated Layer. - next() Layer - - // prev gets a pointer to the Layer encapsulating this one. - prev() Layer - - // setNext sets the pointer to the encapsulated Layer. - setNext(Layer) - - // setPrev sets the pointer to the Layer encapsulating this one. - setPrev(Layer) -} - -// LayerBase is the common elements of all layers. -type LayerBase struct { - nextLayer Layer - prevLayer Layer -} - -func (lb *LayerBase) next() Layer { - return lb.nextLayer -} - -func (lb *LayerBase) prev() Layer { - return lb.prevLayer -} - -func (lb *LayerBase) setNext(l Layer) { - lb.nextLayer = l -} - -func (lb *LayerBase) setPrev(l Layer) { - lb.prevLayer = l -} - -func equalLayer(x, y Layer) bool { - opt := cmp.FilterValues(func(x, y interface{}) bool { - if reflect.ValueOf(x).Kind() == reflect.Ptr && reflect.ValueOf(x).IsNil() { - return true - } - if reflect.ValueOf(y).Kind() == reflect.Ptr && reflect.ValueOf(y).IsNil() { - return true - } - return false - - }, cmp.Ignore()) - return cmp.Equal(x, y, opt, cmpopts.IgnoreUnexported(LayerBase{})) -} - -// Ether can construct and match the ethernet encapsulation. -type Ether struct { - LayerBase - SrcAddr *tcpip.LinkAddress - DstAddr *tcpip.LinkAddress - Type *tcpip.NetworkProtocolNumber -} - -func (l *Ether) toBytes() ([]byte, error) { - b := make([]byte, header.EthernetMinimumSize) - h := header.Ethernet(b) - fields := &header.EthernetFields{} - if l.SrcAddr != nil { - fields.SrcAddr = *l.SrcAddr - } - if l.DstAddr != nil { - fields.DstAddr = *l.DstAddr - } - if l.Type != nil { - fields.Type = *l.Type - } else { - switch n := l.next().(type) { - case *IPv4: - fields.Type = header.IPv4ProtocolNumber - default: - // TODO(b/150301488): Support more protocols, like IPv6. - return nil, fmt.Errorf("can't deduce the ethernet header's next protocol: %d", n) - } - } - h.Encode(fields) - return h, nil -} - -// LinkAddress is a helper routine that allocates a new tcpip.LinkAddress value -// to store v and returns a pointer to it. -func LinkAddress(v tcpip.LinkAddress) *tcpip.LinkAddress { - return &v -} - -// NetworkProtocolNumber is a helper routine that allocates a new -// tcpip.NetworkProtocolNumber value to store v and returns a pointer to it. -func NetworkProtocolNumber(v tcpip.NetworkProtocolNumber) *tcpip.NetworkProtocolNumber { - return &v -} - -// ParseEther parses the bytes assuming that they start with an ethernet header -// and continues parsing further encapsulations. -func ParseEther(b []byte) (Layers, error) { - h := header.Ethernet(b) - ether := Ether{ - SrcAddr: LinkAddress(h.SourceAddress()), - DstAddr: LinkAddress(h.DestinationAddress()), - Type: NetworkProtocolNumber(h.Type()), - } - layers := Layers{ðer} - switch h.Type() { - case header.IPv4ProtocolNumber: - moreLayers, err := ParseIPv4(b[ether.length():]) - if err != nil { - return nil, err - } - return append(layers, moreLayers...), nil - default: - // TODO(b/150301488): Support more protocols, like IPv6. - return nil, fmt.Errorf("can't deduce the ethernet header's next protocol: %v", b) - } -} - -func (l *Ether) match(other Layer) bool { - return equalLayer(l, other) -} - -func (l *Ether) length() int { - return header.EthernetMinimumSize -} - -// IPv4 can construct and match the ethernet excapulation. -type IPv4 struct { - LayerBase - IHL *uint8 - TOS *uint8 - TotalLength *uint16 - ID *uint16 - Flags *uint8 - FragmentOffset *uint16 - TTL *uint8 - Protocol *uint8 - Checksum *uint16 - SrcAddr *tcpip.Address - DstAddr *tcpip.Address -} - -func (l *IPv4) toBytes() ([]byte, error) { - b := make([]byte, header.IPv4MinimumSize) - h := header.IPv4(b) - fields := &header.IPv4Fields{ - IHL: 20, - TOS: 0, - TotalLength: 0, - ID: 0, - Flags: 0, - FragmentOffset: 0, - TTL: 64, - Protocol: 0, - Checksum: 0, - SrcAddr: tcpip.Address(""), - DstAddr: tcpip.Address(""), - } - if l.TOS != nil { - fields.TOS = *l.TOS - } - if l.TotalLength != nil { - fields.TotalLength = *l.TotalLength - } else { - fields.TotalLength = uint16(l.length()) - current := l.next() - for current != nil { - fields.TotalLength += uint16(current.length()) - current = current.next() - } - } - if l.ID != nil { - fields.ID = *l.ID - } - if l.Flags != nil { - fields.Flags = *l.Flags - } - if l.FragmentOffset != nil { - fields.FragmentOffset = *l.FragmentOffset - } - if l.TTL != nil { - fields.TTL = *l.TTL - } - if l.Protocol != nil { - fields.Protocol = *l.Protocol - } else { - switch n := l.next().(type) { - case *TCP: - fields.Protocol = uint8(header.TCPProtocolNumber) - default: - // TODO(b/150301488): Support more protocols, like UDP. - return nil, fmt.Errorf("can't deduce the ip header's next protocol: %+v", n) - } - } - if l.SrcAddr != nil { - fields.SrcAddr = *l.SrcAddr - } - if l.DstAddr != nil { - fields.DstAddr = *l.DstAddr - } - if l.Checksum != nil { - fields.Checksum = *l.Checksum - } - h.Encode(fields) - if l.Checksum == nil { - h.SetChecksum(^h.CalculateChecksum()) - } - return h, nil -} - -// Uint16 is a helper routine that allocates a new -// uint16 value to store v and returns a pointer to it. -func Uint16(v uint16) *uint16 { - return &v -} - -// Uint8 is a helper routine that allocates a new -// uint8 value to store v and returns a pointer to it. -func Uint8(v uint8) *uint8 { - return &v -} - -// Address is a helper routine that allocates a new tcpip.Address value to store -// v and returns a pointer to it. -func Address(v tcpip.Address) *tcpip.Address { - return &v -} - -// ParseIPv4 parses the bytes assuming that they start with an ipv4 header and -// continues parsing further encapsulations. -func ParseIPv4(b []byte) (Layers, error) { - h := header.IPv4(b) - tos, _ := h.TOS() - ipv4 := IPv4{ - IHL: Uint8(h.HeaderLength()), - TOS: &tos, - TotalLength: Uint16(h.TotalLength()), - ID: Uint16(h.ID()), - Flags: Uint8(h.Flags()), - FragmentOffset: Uint16(h.FragmentOffset()), - TTL: Uint8(h.TTL()), - Protocol: Uint8(h.Protocol()), - Checksum: Uint16(h.Checksum()), - SrcAddr: Address(h.SourceAddress()), - DstAddr: Address(h.DestinationAddress()), - } - layers := Layers{&ipv4} - switch h.Protocol() { - case uint8(header.TCPProtocolNumber): - moreLayers, err := ParseTCP(b[ipv4.length():]) - if err != nil { - return nil, err - } - return append(layers, moreLayers...), nil - } - return nil, fmt.Errorf("can't deduce the ethernet header's next protocol: %d", h.Protocol()) -} - -func (l *IPv4) match(other Layer) bool { - return equalLayer(l, other) -} - -func (l *IPv4) length() int { - if l.IHL == nil { - return header.IPv4MinimumSize - } - return int(*l.IHL) -} - -// TCP can construct and match the TCP excapulation. -type TCP struct { - LayerBase - SrcPort *uint16 - DstPort *uint16 - SeqNum *uint32 - AckNum *uint32 - DataOffset *uint8 - Flags *uint8 - WindowSize *uint16 - Checksum *uint16 - UrgentPointer *uint16 -} - -func (l *TCP) toBytes() ([]byte, error) { - b := make([]byte, header.TCPMinimumSize) - h := header.TCP(b) - if l.SrcPort != nil { - h.SetSourcePort(*l.SrcPort) - } - if l.DstPort != nil { - h.SetDestinationPort(*l.DstPort) - } - if l.SeqNum != nil { - h.SetSequenceNumber(*l.SeqNum) - } - if l.AckNum != nil { - h.SetAckNumber(*l.AckNum) - } - if l.DataOffset != nil { - h.SetDataOffset(*l.DataOffset) - } - if l.Flags != nil { - h.SetFlags(*l.Flags) - } - if l.WindowSize != nil { - h.SetWindowSize(*l.WindowSize) - } - if l.UrgentPointer != nil { - h.SetUrgentPoiner(*l.UrgentPointer) - } - if l.Checksum != nil { - h.SetChecksum(*l.Checksum) - return h, nil - } - if err := setChecksum(&h, l); err != nil { - return nil, err - } - return h, nil -} - -// setChecksum calculates the checksum of the TCP header and sets it in h. -func setChecksum(h *header.TCP, tcp *TCP) error { - h.SetChecksum(0) - tcpLength := uint16(tcp.length()) - current := tcp.next() - for current != nil { - tcpLength += uint16(current.length()) - current = current.next() - } - - var xsum uint16 - switch s := tcp.prev().(type) { - case *IPv4: - xsum = header.PseudoHeaderChecksum(header.TCPProtocolNumber, *s.SrcAddr, *s.DstAddr, tcpLength) - default: - // TODO(b/150301488): Support more protocols, like IPv6. - return fmt.Errorf("can't get src and dst addr from previous layer") - } - current = tcp.next() - for current != nil { - payload, err := current.toBytes() - if err != nil { - return fmt.Errorf("can't get bytes for next header: %s", payload) - } - xsum = header.Checksum(payload, xsum) - current = current.next() - } - h.SetChecksum(^h.CalculateChecksum(xsum)) - return nil -} - -// Uint32 is a helper routine that allocates a new -// uint32 value to store v and returns a pointer to it. -func Uint32(v uint32) *uint32 { - return &v -} - -// ParseTCP parses the bytes assuming that they start with a tcp header and -// continues parsing further encapsulations. -func ParseTCP(b []byte) (Layers, error) { - h := header.TCP(b) - tcp := TCP{ - SrcPort: Uint16(h.SourcePort()), - DstPort: Uint16(h.DestinationPort()), - SeqNum: Uint32(h.SequenceNumber()), - AckNum: Uint32(h.AckNumber()), - DataOffset: Uint8(h.DataOffset()), - Flags: Uint8(h.Flags()), - WindowSize: Uint16(h.WindowSize()), - Checksum: Uint16(h.Checksum()), - UrgentPointer: Uint16(h.UrgentPointer()), - } - layers := Layers{&tcp} - moreLayers, err := ParsePayload(b[tcp.length():]) - if err != nil { - return nil, err - } - return append(layers, moreLayers...), nil -} - -func (l *TCP) match(other Layer) bool { - return equalLayer(l, other) -} - -func (l *TCP) length() int { - if l.DataOffset == nil { - return header.TCPMinimumSize - } - return int(*l.DataOffset) -} - -// merge overrides the values in l with the values from other but only in fields -// where the value is not nil. -func (l *TCP) merge(other TCP) error { - return mergo.Merge(l, other, mergo.WithOverride) -} - -// Payload has bytes beyond OSI layer 4. -type Payload struct { - LayerBase - Bytes []byte -} - -// ParsePayload parses the bytes assuming that they start with a payload and -// continue to the end. There can be no further encapsulations. -func ParsePayload(b []byte) (Layers, error) { - payload := Payload{ - Bytes: b, - } - return Layers{&payload}, nil -} - -func (l *Payload) toBytes() ([]byte, error) { - return l.Bytes, nil -} - -func (l *Payload) match(other Layer) bool { - return equalLayer(l, other) -} - -func (l *Payload) length() int { - return len(l.Bytes) -} - -// Layers is an array of Layer and supports similar functions to Layer. -type Layers []Layer - -func (ls *Layers) toBytes() ([]byte, error) { - for i, l := range *ls { - if i > 0 { - l.setPrev((*ls)[i-1]) - } - if i+1 < len(*ls) { - l.setNext((*ls)[i+1]) - } - } - outBytes := []byte{} - for _, l := range *ls { - layerBytes, err := l.toBytes() - if err != nil { - return nil, err - } - outBytes = append(outBytes, layerBytes...) - } - return outBytes, nil -} - -func (ls *Layers) match(other Layers) bool { - if len(*ls) > len(other) { - return false - } - for i := 0; i < len(*ls); i++ { - if !equalLayer((*ls)[i], other[i]) { - return false - } - } - return true -} diff --git a/test/packetimpact/testbench/rawsockets.go b/test/packetimpact/testbench/rawsockets.go deleted file mode 100644 index 0c7d0f979..000000000 --- a/test/packetimpact/testbench/rawsockets.go +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2020 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package testbench - -import ( - "encoding/binary" - "flag" - "math" - "net" - "testing" - "time" - - "golang.org/x/sys/unix" - "gvisor.dev/gvisor/pkg/usermem" -) - -var device = flag.String("device", "", "local device for test packets") - -// Sniffer can sniff raw packets on the wire. -type Sniffer struct { - t *testing.T - fd int -} - -func htons(x uint16) uint16 { - buf := [2]byte{} - binary.BigEndian.PutUint16(buf[:], x) - return usermem.ByteOrder.Uint16(buf[:]) -} - -// NewSniffer creates a Sniffer connected to *device. -func NewSniffer(t *testing.T) (Sniffer, error) { - flag.Parse() - snifferFd, err := unix.Socket(unix.AF_PACKET, unix.SOCK_RAW, int(htons(unix.ETH_P_ALL))) - if err != nil { - return Sniffer{}, err - } - return Sniffer{ - t: t, - fd: snifferFd, - }, nil -} - -// maxReadSize should be large enough for the maximum frame size in bytes. If a -// packet too large for the buffer arrives, the test will get a fatal error. -const maxReadSize int = 65536 - -// Recv tries to read one frame until the timeout is up. -func (s *Sniffer) Recv(timeout time.Duration) []byte { - deadline := time.Now().Add(timeout) - for { - timeout = deadline.Sub(time.Now()) - if timeout <= 0 { - return nil - } - whole, frac := math.Modf(timeout.Seconds()) - tv := unix.Timeval{ - Sec: int64(whole), - Usec: int64(frac * float64(time.Microsecond/time.Second)), - } - - if err := unix.SetsockoptTimeval(s.fd, unix.SOL_SOCKET, unix.SO_RCVTIMEO, &tv); err != nil { - s.t.Fatalf("can't setsockopt SO_RCVTIMEO: %s", err) - } - - buf := make([]byte, maxReadSize) - nread, _, err := unix.Recvfrom(s.fd, buf, unix.MSG_TRUNC) - if err == unix.EINTR || err == unix.EAGAIN { - // There was a timeout. - continue - } - if err != nil { - s.t.Fatalf("can't read: %s", err) - } - if nread > maxReadSize { - s.t.Fatalf("received a truncated frame of %d bytes", nread) - } - return buf[:nread] - } -} - -// Close the socket that Sniffer is using. -func (s *Sniffer) Close() { - if err := unix.Close(s.fd); err != nil { - s.t.Fatalf("can't close sniffer socket: %s", err) - } - s.fd = -1 -} - -// Injector can inject raw frames. -type Injector struct { - t *testing.T - fd int -} - -// NewInjector creates a new injector on *device. -func NewInjector(t *testing.T) (Injector, error) { - flag.Parse() - ifInfo, err := net.InterfaceByName(*device) - if err != nil { - return Injector{}, err - } - - var haddr [8]byte - copy(haddr[:], ifInfo.HardwareAddr) - sa := unix.SockaddrLinklayer{ - Protocol: unix.ETH_P_IP, - Ifindex: ifInfo.Index, - Halen: uint8(len(ifInfo.HardwareAddr)), - Addr: haddr, - } - - injectFd, err := unix.Socket(unix.AF_PACKET, unix.SOCK_RAW, int(htons(unix.ETH_P_ALL))) - if err != nil { - return Injector{}, err - } - if err := unix.Bind(injectFd, &sa); err != nil { - return Injector{}, err - } - return Injector{ - t: t, - fd: injectFd, - }, nil -} - -// Send a raw frame. -func (i *Injector) Send(b []byte) { - if _, err := unix.Write(i.fd, b); err != nil { - i.t.Fatalf("can't write: %s", err) - } -} - -// Close the underlying socket. -func (i *Injector) Close() { - if err := unix.Close(i.fd); err != nil { - i.t.Fatalf("can't close sniffer socket: %s", err) - } - i.fd = -1 -} |