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 }