gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/packetimpact/testbench/dut.go (about)

     1  // Copyright 2020 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package testbench
    16  
    17  import (
    18  	"context"
    19  	"encoding/binary"
    20  	"fmt"
    21  	"net"
    22  	"testing"
    23  	"time"
    24  
    25  	"golang.org/x/sys/unix"
    26  	"google.golang.org/grpc"
    27  	"google.golang.org/grpc/keepalive"
    28  	"gvisor.dev/gvisor/pkg/abi/linux"
    29  	bin "gvisor.dev/gvisor/pkg/binary"
    30  	"gvisor.dev/gvisor/pkg/hostarch"
    31  	pb "gvisor.dev/gvisor/test/packetimpact/proto/posix_server_go_proto"
    32  )
    33  
    34  // DUT communicates with the DUT to force it to make POSIX calls.
    35  type DUT struct {
    36  	conn        *grpc.ClientConn
    37  	posixServer POSIXClient
    38  	Net         *DUTTestNet
    39  	Uname       *DUTUname
    40  }
    41  
    42  // NewDUT creates a new connection with the DUT over gRPC.
    43  func NewDUT(t *testing.T) DUT {
    44  	t.Helper()
    45  	info := getDUTInfo()
    46  	dut := info.ConnectToDUT(t)
    47  	t.Cleanup(func() {
    48  		dut.TearDownConnection()
    49  		info.release()
    50  	})
    51  	return dut
    52  }
    53  
    54  // ConnectToDUT connects to DUT through gRPC.
    55  func (info *DUTInfo) ConnectToDUT(t *testing.T) DUT {
    56  	t.Helper()
    57  
    58  	n := info.Net
    59  	posixServerAddress := net.JoinHostPort(n.POSIXServerIP.String(), fmt.Sprintf("%d", n.POSIXServerPort))
    60  	conn, err := grpc.Dial(posixServerAddress, grpc.WithInsecure(), grpc.WithKeepaliveParams(keepalive.ClientParameters{Timeout: RPCKeepalive}))
    61  	if err != nil {
    62  		t.Fatalf("failed to grpc.Dial(%s): %s", posixServerAddress, err)
    63  	}
    64  	posixServer := NewPOSIXClient(conn)
    65  	return DUT{
    66  		conn:        conn,
    67  		posixServer: posixServer,
    68  		Net:         n,
    69  		Uname:       info.Uname,
    70  	}
    71  }
    72  
    73  // TearDownConnection closes the underlying connection.
    74  func (dut *DUT) TearDownConnection() {
    75  	dut.conn.Close()
    76  }
    77  
    78  func (dut *DUT) sockaddrToProto(t *testing.T, sa unix.Sockaddr) *pb.Sockaddr {
    79  	t.Helper()
    80  
    81  	switch s := sa.(type) {
    82  	case *unix.SockaddrInet4:
    83  		return &pb.Sockaddr{
    84  			Sockaddr: &pb.Sockaddr_In{
    85  				In: &pb.SockaddrIn{
    86  					Family: unix.AF_INET,
    87  					Port:   uint32(s.Port),
    88  					Addr:   s.Addr[:],
    89  				},
    90  			},
    91  		}
    92  	case *unix.SockaddrInet6:
    93  		return &pb.Sockaddr{
    94  			Sockaddr: &pb.Sockaddr_In6{
    95  				In6: &pb.SockaddrIn6{
    96  					Family:   unix.AF_INET6,
    97  					Port:     uint32(s.Port),
    98  					Flowinfo: 0,
    99  					ScopeId:  s.ZoneId,
   100  					Addr:     s.Addr[:],
   101  				},
   102  			},
   103  		}
   104  	}
   105  	t.Fatalf("can't parse Sockaddr struct: %+v", sa)
   106  	return nil
   107  }
   108  
   109  func (dut *DUT) protoToSockaddr(t *testing.T, sa *pb.Sockaddr) unix.Sockaddr {
   110  	t.Helper()
   111  
   112  	switch s := sa.Sockaddr.(type) {
   113  	case *pb.Sockaddr_In:
   114  		ret := unix.SockaddrInet4{
   115  			Port: int(s.In.GetPort()),
   116  		}
   117  		copy(ret.Addr[:], s.In.GetAddr())
   118  		return &ret
   119  	case *pb.Sockaddr_In6:
   120  		ret := unix.SockaddrInet6{
   121  			Port:   int(s.In6.GetPort()),
   122  			ZoneId: s.In6.GetScopeId(),
   123  		}
   124  		copy(ret.Addr[:], s.In6.GetAddr())
   125  		return &ret
   126  	}
   127  	t.Fatalf("can't parse Sockaddr proto: %#v", sa)
   128  	return nil
   129  }
   130  
   131  // CreateBoundSocket makes a new socket on the DUT, with type typ and protocol
   132  // proto, and bound to the IP address addr. Returns the new file descriptor and
   133  // the port that was selected on the DUT.
   134  func (dut *DUT) CreateBoundSocket(t *testing.T, typ, proto int32, addr net.IP) (int32, uint16) {
   135  	t.Helper()
   136  
   137  	var fd int32
   138  	if addr.To4() != nil {
   139  		fd = dut.Socket(t, unix.AF_INET, typ, proto)
   140  		sa := unix.SockaddrInet4{}
   141  		copy(sa.Addr[:], addr.To4())
   142  		dut.Bind(t, fd, &sa)
   143  	} else if addr.To16() != nil {
   144  		fd = dut.Socket(t, unix.AF_INET6, typ, proto)
   145  		sa := unix.SockaddrInet6{}
   146  		copy(sa.Addr[:], addr.To16())
   147  		sa.ZoneId = dut.Net.RemoteDevID
   148  		dut.Bind(t, fd, &sa)
   149  	} else {
   150  		t.Fatalf("invalid IP address: %s", addr)
   151  	}
   152  	sa := dut.GetSockName(t, fd)
   153  	var port int
   154  	switch s := sa.(type) {
   155  	case *unix.SockaddrInet4:
   156  		port = s.Port
   157  	case *unix.SockaddrInet6:
   158  		port = s.Port
   159  	default:
   160  		t.Fatalf("unknown sockaddr type from getsockname: %T", sa)
   161  	}
   162  	return fd, uint16(port)
   163  }
   164  
   165  // CreateListener makes a new TCP connection. If it fails, the test ends.
   166  func (dut *DUT) CreateListener(t *testing.T, typ, proto, backlog int32) (int32, uint16) {
   167  	t.Helper()
   168  
   169  	fd, remotePort := dut.CreateBoundSocket(t, typ, proto, dut.Net.RemoteIPv4)
   170  	dut.Listen(t, fd, backlog)
   171  	return fd, remotePort
   172  }
   173  
   174  // All the functions that make gRPC calls to the POSIX service are below, sorted
   175  // alphabetically.
   176  
   177  // Accept calls accept on the DUT and causes a fatal test failure if it doesn't
   178  // succeed. If more control over the timeout or error handling is needed, use
   179  // AcceptWithErrno.
   180  func (dut *DUT) Accept(t *testing.T, sockfd int32) (int32, unix.Sockaddr) {
   181  	t.Helper()
   182  
   183  	fd, sa, err := dut.AcceptWithErrno(context.Background(), t, sockfd)
   184  	if fd < 0 {
   185  		t.Fatalf("failed to accept: %s", err)
   186  	}
   187  	return fd, sa
   188  }
   189  
   190  // AcceptWithErrno calls accept on the DUT.
   191  func (dut *DUT) AcceptWithErrno(ctx context.Context, t *testing.T, sockfd int32) (int32, unix.Sockaddr, error) {
   192  	t.Helper()
   193  
   194  	req := &pb.AcceptRequest{
   195  		Sockfd: sockfd,
   196  	}
   197  	resp, err := dut.posixServer.Accept(ctx, req)
   198  	if err != nil {
   199  		t.Fatalf("failed to call Accept: %s", err)
   200  	}
   201  	return resp.GetFd(), dut.protoToSockaddr(t, resp.GetAddr()), unix.Errno(resp.GetErrno_())
   202  }
   203  
   204  // Bind calls bind on the DUT and causes a fatal test failure if it doesn't
   205  // succeed. If more control over the timeout or error handling is
   206  // needed, use BindWithErrno.
   207  func (dut *DUT) Bind(t *testing.T, fd int32, sa unix.Sockaddr) {
   208  	t.Helper()
   209  
   210  	ret, err := dut.BindWithErrno(context.Background(), t, fd, sa)
   211  	if ret != 0 {
   212  		t.Fatalf("failed to bind socket: %s", err)
   213  	}
   214  }
   215  
   216  // BindWithErrno calls bind on the DUT.
   217  func (dut *DUT) BindWithErrno(ctx context.Context, t *testing.T, fd int32, sa unix.Sockaddr) (int32, error) {
   218  	t.Helper()
   219  
   220  	req := &pb.BindRequest{
   221  		Sockfd: fd,
   222  		Addr:   dut.sockaddrToProto(t, sa),
   223  	}
   224  	resp, err := dut.posixServer.Bind(ctx, req)
   225  	if err != nil {
   226  		t.Fatalf("failed to call Bind: %s", err)
   227  	}
   228  	return resp.GetRet(), unix.Errno(resp.GetErrno_())
   229  }
   230  
   231  // Close calls close on the DUT and causes a fatal test failure if it doesn't
   232  // succeed. If more control over the timeout or error handling is needed, use
   233  // CloseWithErrno.
   234  func (dut *DUT) Close(t *testing.T, fd int32) {
   235  	t.Helper()
   236  
   237  	ret, err := dut.CloseWithErrno(context.Background(), t, fd)
   238  	if ret != 0 {
   239  		t.Fatalf("failed to close: %s", err)
   240  	}
   241  }
   242  
   243  // CloseWithErrno calls close on the DUT.
   244  func (dut *DUT) CloseWithErrno(ctx context.Context, t *testing.T, fd int32) (int32, error) {
   245  	t.Helper()
   246  
   247  	req := &pb.CloseRequest{
   248  		Fd: fd,
   249  	}
   250  	resp, err := dut.posixServer.Close(ctx, req)
   251  	if err != nil {
   252  		t.Fatalf("failed to call Close: %s", err)
   253  	}
   254  	return resp.GetRet(), unix.Errno(resp.GetErrno_())
   255  }
   256  
   257  // Connect calls connect on the DUT and causes a fatal test failure if it
   258  // doesn't succeed. If more control over the timeout or error handling is
   259  // needed, use ConnectWithErrno.
   260  func (dut *DUT) Connect(t *testing.T, fd int32, sa unix.Sockaddr) {
   261  	t.Helper()
   262  
   263  	ret, err := dut.ConnectWithErrno(context.Background(), t, fd, sa)
   264  	// Ignore 'operation in progress' error that can be returned when the socket
   265  	// is non-blocking.
   266  	if err != unix.EINPROGRESS && ret != 0 {
   267  		t.Fatalf("failed to connect socket: %s", err)
   268  	}
   269  }
   270  
   271  // ConnectWithErrno calls bind on the DUT.
   272  func (dut *DUT) ConnectWithErrno(ctx context.Context, t *testing.T, fd int32, sa unix.Sockaddr) (int32, error) {
   273  	t.Helper()
   274  
   275  	req := &pb.ConnectRequest{
   276  		Sockfd: fd,
   277  		Addr:   dut.sockaddrToProto(t, sa),
   278  	}
   279  	resp, err := dut.posixServer.Connect(ctx, req)
   280  	if err != nil {
   281  		t.Fatalf("failed to call Connect: %s", err)
   282  	}
   283  	return resp.GetRet(), unix.Errno(resp.GetErrno_())
   284  }
   285  
   286  // GetSockName calls getsockname on the DUT and causes a fatal test failure if
   287  // it doesn't succeed. If more control over the timeout or error handling is
   288  // needed, use GetSockNameWithErrno.
   289  func (dut *DUT) GetSockName(t *testing.T, sockfd int32) unix.Sockaddr {
   290  	t.Helper()
   291  
   292  	ret, sa, err := dut.GetSockNameWithErrno(context.Background(), t, sockfd)
   293  	if ret != 0 {
   294  		t.Fatalf("failed to getsockname: %s", err)
   295  	}
   296  	return sa
   297  }
   298  
   299  // GetSockNameWithErrno calls getsockname on the DUT.
   300  func (dut *DUT) GetSockNameWithErrno(ctx context.Context, t *testing.T, sockfd int32) (int32, unix.Sockaddr, error) {
   301  	t.Helper()
   302  
   303  	req := &pb.GetSockNameRequest{
   304  		Sockfd: sockfd,
   305  	}
   306  	resp, err := dut.posixServer.GetSockName(ctx, req)
   307  	if err != nil {
   308  		t.Fatalf("failed to call Bind: %s", err)
   309  	}
   310  	return resp.GetRet(), dut.protoToSockaddr(t, resp.GetAddr()), unix.Errno(resp.GetErrno_())
   311  }
   312  
   313  func (dut *DUT) getSockOpt(ctx context.Context, t *testing.T, sockfd, level, optname, optlen int32, typ pb.GetSockOptRequest_SockOptType) (int32, *pb.SockOptVal, error) {
   314  	t.Helper()
   315  
   316  	req := &pb.GetSockOptRequest{
   317  		Sockfd:  sockfd,
   318  		Level:   level,
   319  		Optname: optname,
   320  		Optlen:  optlen,
   321  		Type:    typ,
   322  	}
   323  	resp, err := dut.posixServer.GetSockOpt(ctx, req)
   324  	if err != nil {
   325  		t.Fatalf("failed to call GetSockOpt: %s", err)
   326  	}
   327  	optval := resp.GetOptval()
   328  	if optval == nil {
   329  		t.Fatalf("GetSockOpt response does not contain a value")
   330  	}
   331  	return resp.GetRet(), optval, unix.Errno(resp.GetErrno_())
   332  }
   333  
   334  // GetSockOpt calls getsockopt on the DUT and causes a fatal test failure if it
   335  // doesn't succeed. If more control over the timeout or error handling is
   336  // needed, use GetSockOptWithErrno. Because endianess and the width of values
   337  // might differ between the testbench and DUT architectures, prefer to use a
   338  // more specific GetSockOptXxx function.
   339  func (dut *DUT) GetSockOpt(t *testing.T, sockfd, level, optname, optlen int32) []byte {
   340  	t.Helper()
   341  
   342  	ret, optval, err := dut.GetSockOptWithErrno(context.Background(), t, sockfd, level, optname, optlen)
   343  	if ret != 0 {
   344  		t.Fatalf("failed to GetSockOpt: %s", err)
   345  	}
   346  	return optval
   347  }
   348  
   349  // GetSockOptWithErrno calls getsockopt on the DUT. Because endianess and the
   350  // width of values might differ between the testbench and DUT architectures,
   351  // prefer to use a more specific GetSockOptXxxWithErrno function.
   352  func (dut *DUT) GetSockOptWithErrno(ctx context.Context, t *testing.T, sockfd, level, optname, optlen int32) (int32, []byte, error) {
   353  	t.Helper()
   354  
   355  	ret, optval, errno := dut.getSockOpt(ctx, t, sockfd, level, optname, optlen, pb.GetSockOptRequest_BYTES)
   356  	bytesval, ok := optval.Val.(*pb.SockOptVal_Bytesval)
   357  	if !ok {
   358  		t.Fatalf("GetSockOpt got value type: %T, want bytes", optval.Val)
   359  	}
   360  	return ret, bytesval.Bytesval, errno
   361  }
   362  
   363  // GetSockOptInt calls getsockopt on the DUT and causes a fatal test failure
   364  // if it doesn't succeed. If more control over the int optval or error handling
   365  // is needed, use GetSockOptIntWithErrno.
   366  func (dut *DUT) GetSockOptInt(t *testing.T, sockfd, level, optname int32) int32 {
   367  	t.Helper()
   368  
   369  	ret, intval, err := dut.GetSockOptIntWithErrno(context.Background(), t, sockfd, level, optname)
   370  	if ret != 0 {
   371  		t.Fatalf("failed to GetSockOptInt: %s", err)
   372  	}
   373  	return intval
   374  }
   375  
   376  // GetSockOptIntWithErrno calls getsockopt with an integer optval.
   377  func (dut *DUT) GetSockOptIntWithErrno(ctx context.Context, t *testing.T, sockfd, level, optname int32) (int32, int32, error) {
   378  	t.Helper()
   379  
   380  	ret, optval, errno := dut.getSockOpt(ctx, t, sockfd, level, optname, 0, pb.GetSockOptRequest_INT)
   381  	intval, ok := optval.Val.(*pb.SockOptVal_Intval)
   382  	if !ok {
   383  		t.Fatalf("GetSockOpt got value type: %T, want int", optval.Val)
   384  	}
   385  	return ret, intval.Intval, errno
   386  }
   387  
   388  // GetSockOptTimeval calls getsockopt on the DUT and causes a fatal test failure
   389  // if it doesn't succeed. If more control over the timeout or error handling is
   390  // needed, use GetSockOptTimevalWithErrno.
   391  func (dut *DUT) GetSockOptTimeval(t *testing.T, sockfd, level, optname int32) unix.Timeval {
   392  	t.Helper()
   393  
   394  	ret, timeval, err := dut.GetSockOptTimevalWithErrno(context.Background(), t, sockfd, level, optname)
   395  	if ret != 0 {
   396  		t.Fatalf("failed to GetSockOptTimeval: %s", err)
   397  	}
   398  	return timeval
   399  }
   400  
   401  // GetSockOptTimevalWithErrno calls getsockopt and returns a timeval.
   402  func (dut *DUT) GetSockOptTimevalWithErrno(ctx context.Context, t *testing.T, sockfd, level, optname int32) (int32, unix.Timeval, error) {
   403  	t.Helper()
   404  
   405  	ret, optval, errno := dut.getSockOpt(ctx, t, sockfd, level, optname, 0, pb.GetSockOptRequest_TIME)
   406  	tv, ok := optval.Val.(*pb.SockOptVal_Timeval)
   407  	if !ok {
   408  		t.Fatalf("GetSockOpt got value type: %T, want timeval", optval.Val)
   409  	}
   410  	timeval := unix.Timeval{
   411  		Sec:  tv.Timeval.Seconds,
   412  		Usec: tv.Timeval.Microseconds,
   413  	}
   414  	return ret, timeval, errno
   415  }
   416  
   417  // GetSockOptTCPInfo retreives TCPInfo for the given socket descriptor.
   418  func (dut *DUT) GetSockOptTCPInfo(t *testing.T, sockfd int32) linux.TCPInfo {
   419  	t.Helper()
   420  
   421  	ret, info, err := dut.GetSockOptTCPInfoWithErrno(context.Background(), t, sockfd)
   422  	if ret != 0 || err != unix.Errno(0) {
   423  		t.Fatalf("failed to GetSockOptTCPInfo: %s", err)
   424  	}
   425  	return info
   426  }
   427  
   428  // GetSockOptTCPInfoWithErrno retreives TCPInfo with any errno.
   429  func (dut *DUT) GetSockOptTCPInfoWithErrno(ctx context.Context, t *testing.T, sockfd int32) (int32, linux.TCPInfo, error) {
   430  	t.Helper()
   431  
   432  	info := linux.TCPInfo{}
   433  	ret, infoBytes, errno := dut.GetSockOptWithErrno(ctx, t, sockfd, unix.SOL_TCP, unix.TCP_INFO, int32(linux.SizeOfTCPInfo))
   434  	if got, want := len(infoBytes), linux.SizeOfTCPInfo; got != want {
   435  		t.Fatalf("expected %T, got %d bytes want %d bytes", info, got, want)
   436  	}
   437  	bin.Unmarshal(infoBytes, hostarch.ByteOrder, &info)
   438  
   439  	return ret, info, errno
   440  }
   441  
   442  // Listen calls listen on the DUT and causes a fatal test failure if it doesn't
   443  // succeed. If more control over the timeout or error handling is needed, use
   444  // ListenWithErrno.
   445  func (dut *DUT) Listen(t *testing.T, sockfd, backlog int32) {
   446  	t.Helper()
   447  
   448  	ret, err := dut.ListenWithErrno(context.Background(), t, sockfd, backlog)
   449  	if ret != 0 {
   450  		t.Fatalf("failed to listen: %s", err)
   451  	}
   452  }
   453  
   454  // ListenWithErrno calls listen on the DUT.
   455  func (dut *DUT) ListenWithErrno(ctx context.Context, t *testing.T, sockfd, backlog int32) (int32, error) {
   456  	t.Helper()
   457  
   458  	req := &pb.ListenRequest{
   459  		Sockfd:  sockfd,
   460  		Backlog: backlog,
   461  	}
   462  	resp, err := dut.posixServer.Listen(ctx, req)
   463  	if err != nil {
   464  		t.Fatalf("failed to call Listen: %s", err)
   465  	}
   466  	return resp.GetRet(), unix.Errno(resp.GetErrno_())
   467  }
   468  
   469  // PollOne calls poll on the DUT and asserts that the expected event must be
   470  // signaled on the given fd within the given timeout.
   471  func (dut *DUT) PollOne(t *testing.T, fd int32, events int16, timeout time.Duration) {
   472  	t.Helper()
   473  
   474  	pfds := dut.Poll(t, []unix.PollFd{{Fd: fd, Events: events}}, timeout)
   475  	if n := len(pfds); n != 1 {
   476  		t.Fatalf("Poll returned %d ready file descriptors, expected 1", n)
   477  	}
   478  	if readyFd := pfds[0].Fd; readyFd != fd {
   479  		t.Fatalf("Poll returned an fd %d that was not requested (%d)", readyFd, fd)
   480  	}
   481  	if got, want := pfds[0].Revents, int16(events); got&want != want {
   482  		t.Fatalf("Poll returned events does not include all of the interested events, got: %#b, want: %#b", got, want)
   483  	}
   484  }
   485  
   486  // Poll calls poll on the DUT and causes a fatal test failure if it doesn't
   487  // succeed. If more control over error handling is needed, use PollWithErrno.
   488  // Only pollfds with non-empty revents are returned, the only way to tie the
   489  // response back to the original request is using the fd number.
   490  func (dut *DUT) Poll(t *testing.T, pfds []unix.PollFd, timeout time.Duration) []unix.PollFd {
   491  	t.Helper()
   492  
   493  	ret, result, err := dut.PollWithErrno(context.Background(), t, pfds, timeout)
   494  	if ret < 0 {
   495  		t.Fatalf("failed to poll: %s", err)
   496  	}
   497  	return result
   498  }
   499  
   500  // PollWithErrno calls poll on the DUT.
   501  func (dut *DUT) PollWithErrno(ctx context.Context, t *testing.T, pfds []unix.PollFd, timeout time.Duration) (int32, []unix.PollFd, error) {
   502  	t.Helper()
   503  
   504  	req := &pb.PollRequest{
   505  		TimeoutMillis: int32(timeout.Milliseconds()),
   506  	}
   507  	for _, pfd := range pfds {
   508  		req.Pfds = append(req.Pfds, &pb.PollFd{
   509  			Fd:     pfd.Fd,
   510  			Events: uint32(pfd.Events),
   511  		})
   512  	}
   513  	resp, err := dut.posixServer.Poll(ctx, req)
   514  	if err != nil {
   515  		t.Fatalf("failed to call Poll: %s", err)
   516  	}
   517  	if ret, npfds := resp.GetRet(), len(resp.GetPfds()); ret >= 0 && int(ret) != npfds {
   518  		t.Fatalf("nonsensical poll response: ret(%d) != len(pfds)(%d)", ret, npfds)
   519  	}
   520  	var result []unix.PollFd
   521  	for _, protoPfd := range resp.GetPfds() {
   522  		result = append(result, unix.PollFd{
   523  			Fd:      protoPfd.GetFd(),
   524  			Revents: int16(protoPfd.GetEvents()),
   525  		})
   526  	}
   527  	return resp.GetRet(), result, unix.Errno(resp.GetErrno_())
   528  }
   529  
   530  // Send calls send on the DUT and causes a fatal test failure if it doesn't
   531  // succeed. If more control over the timeout or error handling is needed, use
   532  // SendWithErrno.
   533  func (dut *DUT) Send(t *testing.T, sockfd int32, buf []byte, flags int32) int32 {
   534  	t.Helper()
   535  
   536  	ret, err := dut.SendWithErrno(context.Background(), t, sockfd, buf, flags)
   537  	if ret == -1 {
   538  		t.Fatalf("failed to send: %s", err)
   539  	}
   540  	return ret
   541  }
   542  
   543  // SendWithErrno calls send on the DUT.
   544  func (dut *DUT) SendWithErrno(ctx context.Context, t *testing.T, sockfd int32, buf []byte, flags int32) (int32, error) {
   545  	t.Helper()
   546  
   547  	req := &pb.SendRequest{
   548  		Sockfd: sockfd,
   549  		Buf:    buf,
   550  		Flags:  flags,
   551  	}
   552  	resp, err := dut.posixServer.Send(ctx, req)
   553  	if err != nil {
   554  		t.Fatalf("failed to call Send: %s", err)
   555  	}
   556  	return resp.GetRet(), unix.Errno(resp.GetErrno_())
   557  }
   558  
   559  // SendTo calls sendto on the DUT and causes a fatal test failure if it doesn't
   560  // succeed. If more control over the timeout or error handling is needed, use
   561  // SendToWithErrno.
   562  func (dut *DUT) SendTo(t *testing.T, sockfd int32, buf []byte, flags int32, destAddr unix.Sockaddr) int32 {
   563  	t.Helper()
   564  
   565  	ret, err := dut.SendToWithErrno(context.Background(), t, sockfd, buf, flags, destAddr)
   566  	if ret == -1 {
   567  		t.Fatalf("failed to sendto: %s", err)
   568  	}
   569  	return ret
   570  }
   571  
   572  // SendToWithErrno calls sendto on the DUT.
   573  func (dut *DUT) SendToWithErrno(ctx context.Context, t *testing.T, sockfd int32, buf []byte, flags int32, destAddr unix.Sockaddr) (int32, error) {
   574  	t.Helper()
   575  
   576  	req := &pb.SendToRequest{
   577  		Sockfd:   sockfd,
   578  		Buf:      buf,
   579  		Flags:    flags,
   580  		DestAddr: dut.sockaddrToProto(t, destAddr),
   581  	}
   582  	resp, err := dut.posixServer.SendTo(ctx, req)
   583  	if err != nil {
   584  		t.Fatalf("failed to call SendTo: %s", err)
   585  	}
   586  	return resp.GetRet(), unix.Errno(resp.GetErrno_())
   587  }
   588  
   589  // SetNonBlocking will set O_NONBLOCK flag for fd if nonblocking
   590  // is true, otherwise it will clear the flag.
   591  func (dut *DUT) SetNonBlocking(t *testing.T, fd int32, nonblocking bool) {
   592  	t.Helper()
   593  
   594  	req := &pb.SetNonblockingRequest{
   595  		Fd:          fd,
   596  		Nonblocking: nonblocking,
   597  	}
   598  
   599  	resp, err := dut.posixServer.SetNonblocking(context.Background(), req)
   600  	if err != nil {
   601  		t.Fatalf("failed to call SetNonblocking: %s", err)
   602  	}
   603  	if resp.GetRet() == -1 {
   604  		t.Fatalf("fcntl(%d, %s) failed: %s", fd, resp.GetCmd(), unix.Errno(resp.GetErrno_()))
   605  	}
   606  }
   607  
   608  func (dut *DUT) setSockOpt(ctx context.Context, t *testing.T, sockfd, level, optname int32, optval *pb.SockOptVal) (int32, error) {
   609  	t.Helper()
   610  
   611  	req := &pb.SetSockOptRequest{
   612  		Sockfd:  sockfd,
   613  		Level:   level,
   614  		Optname: optname,
   615  		Optval:  optval,
   616  	}
   617  	resp, err := dut.posixServer.SetSockOpt(ctx, req)
   618  	if err != nil {
   619  		t.Fatalf("failed to call SetSockOpt: %s", err)
   620  	}
   621  	return resp.GetRet(), unix.Errno(resp.GetErrno_())
   622  }
   623  
   624  // SetSockOpt calls setsockopt on the DUT and causes a fatal test failure if it
   625  // doesn't succeed. If more control over the timeout or error handling is
   626  // needed, use SetSockOptWithErrno. Because endianess and the width of values
   627  // might differ between the testbench and DUT architectures, prefer to use a
   628  // more specific SetSockOptXxx function.
   629  func (dut *DUT) SetSockOpt(t *testing.T, sockfd, level, optname int32, optval []byte) {
   630  	t.Helper()
   631  
   632  	ret, err := dut.SetSockOptWithErrno(context.Background(), t, sockfd, level, optname, optval)
   633  	if ret != 0 {
   634  		t.Fatalf("failed to SetSockOpt: %s", err)
   635  	}
   636  }
   637  
   638  // SetSockOptWithErrno calls setsockopt on the DUT. Because endianess and the
   639  // width of values might differ between the testbench and DUT architectures,
   640  // prefer to use a more specific SetSockOptXxxWithErrno function.
   641  func (dut *DUT) SetSockOptWithErrno(ctx context.Context, t *testing.T, sockfd, level, optname int32, optval []byte) (int32, error) {
   642  	t.Helper()
   643  
   644  	return dut.setSockOpt(ctx, t, sockfd, level, optname, &pb.SockOptVal{Val: &pb.SockOptVal_Bytesval{optval}})
   645  }
   646  
   647  // SetSockOptInt calls setsockopt on the DUT and causes a fatal test failure
   648  // if it doesn't succeed. If more control over the int optval or error handling
   649  // is needed, use SetSockOptIntWithErrno.
   650  func (dut *DUT) SetSockOptInt(t *testing.T, sockfd, level, optname, optval int32) {
   651  	t.Helper()
   652  
   653  	ret, err := dut.SetSockOptIntWithErrno(context.Background(), t, sockfd, level, optname, optval)
   654  	if ret != 0 {
   655  		t.Fatalf("failed to SetSockOptInt: %s", err)
   656  	}
   657  }
   658  
   659  // SetSockOptIntWithErrno calls setsockopt with an integer optval.
   660  func (dut *DUT) SetSockOptIntWithErrno(ctx context.Context, t *testing.T, sockfd, level, optname, optval int32) (int32, error) {
   661  	t.Helper()
   662  
   663  	return dut.setSockOpt(ctx, t, sockfd, level, optname, &pb.SockOptVal{Val: &pb.SockOptVal_Intval{optval}})
   664  }
   665  
   666  // SetSockOptTimeval calls setsockopt on the DUT and causes a fatal test failure
   667  // if it doesn't succeed. If more control over the timeout or error handling is
   668  // needed, use SetSockOptTimevalWithErrno.
   669  func (dut *DUT) SetSockOptTimeval(t *testing.T, sockfd, level, optname int32, tv *unix.Timeval) {
   670  	t.Helper()
   671  
   672  	ret, err := dut.SetSockOptTimevalWithErrno(context.Background(), t, sockfd, level, optname, tv)
   673  	if ret != 0 {
   674  		t.Fatalf("failed to SetSockOptTimeval: %s", err)
   675  	}
   676  }
   677  
   678  // SetSockOptTimevalWithErrno calls setsockopt with the timeval converted to
   679  // bytes.
   680  func (dut *DUT) SetSockOptTimevalWithErrno(ctx context.Context, t *testing.T, sockfd, level, optname int32, tv *unix.Timeval) (int32, error) {
   681  	t.Helper()
   682  
   683  	timeval := pb.Timeval{
   684  		Seconds:      int64(tv.Sec),
   685  		Microseconds: int64(tv.Usec),
   686  	}
   687  	return dut.setSockOpt(ctx, t, sockfd, level, optname, &pb.SockOptVal{Val: &pb.SockOptVal_Timeval{&timeval}})
   688  }
   689  
   690  // Socket calls socket on the DUT and returns the file descriptor. If socket
   691  // fails on the DUT, the test ends.
   692  func (dut *DUT) Socket(t *testing.T, domain, typ, proto int32) int32 {
   693  	t.Helper()
   694  
   695  	fd, err := dut.SocketWithErrno(t, domain, typ, proto)
   696  	if fd < 0 {
   697  		t.Fatalf("failed to create socket: %s", err)
   698  	}
   699  	return fd
   700  }
   701  
   702  // SocketWithErrno calls socket on the DUT and returns the fd and errno.
   703  func (dut *DUT) SocketWithErrno(t *testing.T, domain, typ, proto int32) (int32, error) {
   704  	t.Helper()
   705  
   706  	req := &pb.SocketRequest{
   707  		Domain:   domain,
   708  		Type:     typ,
   709  		Protocol: proto,
   710  	}
   711  	resp, err := dut.posixServer.Socket(context.Background(), req)
   712  	if err != nil {
   713  		t.Fatalf("failed to call Socket: %s", err)
   714  	}
   715  	return resp.GetFd(), unix.Errno(resp.GetErrno_())
   716  }
   717  
   718  // Recv calls recv on the DUT and causes a fatal test failure if it doesn't
   719  // succeed. If more control over the timeout or error handling is needed, use
   720  // RecvWithErrno.
   721  func (dut *DUT) Recv(t *testing.T, sockfd, len, flags int32) []byte {
   722  	t.Helper()
   723  
   724  	ret, buf, err := dut.RecvWithErrno(context.Background(), t, sockfd, len, flags)
   725  	if ret == -1 {
   726  		t.Fatalf("failed to recv: %s", err)
   727  	}
   728  	return buf
   729  }
   730  
   731  // RecvWithErrno calls recv on the DUT.
   732  func (dut *DUT) RecvWithErrno(ctx context.Context, t *testing.T, sockfd, len, flags int32) (int32, []byte, error) {
   733  	t.Helper()
   734  
   735  	req := &pb.RecvRequest{
   736  		Sockfd: sockfd,
   737  		Len:    len,
   738  		Flags:  flags,
   739  	}
   740  	resp, err := dut.posixServer.Recv(ctx, req)
   741  	if err != nil {
   742  		t.Fatalf("failed to call Recv: %s", err)
   743  	}
   744  	return resp.GetRet(), resp.GetBuf(), unix.Errno(resp.GetErrno_())
   745  }
   746  
   747  // SetSockLingerOption sets SO_LINGER socket option on the DUT.
   748  func (dut *DUT) SetSockLingerOption(t *testing.T, sockfd int32, timeout time.Duration, enable bool) {
   749  	var linger unix.Linger
   750  	if enable {
   751  		linger.Onoff = 1
   752  	}
   753  	linger.Linger = int32(timeout / time.Second)
   754  
   755  	buf := make([]byte, 8)
   756  	binary.LittleEndian.PutUint32(buf, uint32(linger.Onoff))
   757  	binary.LittleEndian.PutUint32(buf[4:], uint32(linger.Linger))
   758  	dut.SetSockOpt(t, sockfd, unix.SOL_SOCKET, unix.SO_LINGER, buf)
   759  }
   760  
   761  // Shutdown calls shutdown on the DUT and causes a fatal test failure if it
   762  // doesn't succeed. If more control over the timeout or error handling is
   763  // needed, use ShutdownWithErrno.
   764  func (dut *DUT) Shutdown(t *testing.T, fd, how int32) {
   765  	t.Helper()
   766  
   767  	ret, err := dut.ShutdownWithErrno(context.Background(), t, fd, how)
   768  	if ret != 0 {
   769  		t.Fatalf("failed to shutdown(%d, %d): %s", fd, how, err)
   770  	}
   771  }
   772  
   773  // ShutdownWithErrno calls shutdown on the DUT.
   774  func (dut *DUT) ShutdownWithErrno(ctx context.Context, t *testing.T, fd, how int32) (int32, error) {
   775  	t.Helper()
   776  
   777  	req := &pb.ShutdownRequest{
   778  		Fd:  fd,
   779  		How: how,
   780  	}
   781  	resp, err := dut.posixServer.Shutdown(ctx, req)
   782  	if err != nil {
   783  		t.Fatalf("failed to call Shutdown: %s", err)
   784  	}
   785  	if resp.GetErrno_() == 0 {
   786  		return resp.GetRet(), nil
   787  	}
   788  	return resp.GetRet(), unix.Errno(resp.GetErrno_())
   789  }