2023-10-11 14:44:29 +08:00
|
|
|
// Copyright 2023 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 stack
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"syscall"
|
|
|
|
|
|
|
|
|
|
"gvisor.dev/gvisor/pkg/abi/linux"
|
|
|
|
|
"gvisor.dev/gvisor/pkg/abi/linux/errno"
|
|
|
|
|
"gvisor.dev/gvisor/pkg/binary"
|
|
|
|
|
"gvisor.dev/gvisor/pkg/context"
|
|
|
|
|
"gvisor.dev/gvisor/pkg/errors/linuxerr"
|
|
|
|
|
"gvisor.dev/gvisor/pkg/hostarch"
|
|
|
|
|
"gvisor.dev/gvisor/pkg/marshal"
|
|
|
|
|
"gvisor.dev/gvisor/pkg/marshal/primitive"
|
|
|
|
|
"gvisor.dev/gvisor/pkg/safemem"
|
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/arch"
|
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/fsimpl/sockfs"
|
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/inet"
|
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
2024-11-06 18:08:21 -08:00
|
|
|
"gvisor.dev/gvisor/pkg/sentry/ktime"
|
2023-10-11 14:44:29 +08:00
|
|
|
"gvisor.dev/gvisor/pkg/sentry/socket"
|
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/socket/plugin"
|
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/socket/plugin/cgo"
|
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/unimpl"
|
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
|
|
|
|
"gvisor.dev/gvisor/pkg/syserr"
|
|
|
|
|
"gvisor.dev/gvisor/pkg/usermem"
|
|
|
|
|
"gvisor.dev/gvisor/pkg/waiter"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type socketOperations struct {
|
|
|
|
|
vfsfd vfs.FileDescription
|
|
|
|
|
vfs.FileDescriptionDefaultImpl
|
|
|
|
|
vfs.DentryMetadataFileDescriptionImpl
|
|
|
|
|
vfs.LockFD
|
|
|
|
|
socket.SendReceiveTimeout
|
|
|
|
|
|
|
|
|
|
family int
|
|
|
|
|
skType linux.SockType
|
|
|
|
|
protocol int
|
|
|
|
|
|
|
|
|
|
// fd holds the current socket fd created by plugin stack.
|
|
|
|
|
fd uint32 `state:"nosave"`
|
|
|
|
|
|
|
|
|
|
// eventInfo holds current socket fd's corresponding notification queue
|
|
|
|
|
// and events status. It will be used to interact with plugin
|
|
|
|
|
// network stack for event reporting.
|
|
|
|
|
eventInfo plugin.EventInfo
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var _ = socket.Socket(&socketOperations{})
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
sizeofSockaddr = syscall.SizeofSockaddrInet6
|
|
|
|
|
|
|
|
|
|
// Lo IF index.
|
|
|
|
|
ifLoIndex = 1
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func newSocket(t *kernel.Task, family int, skType linux.SockType, protocol int, notifier *Notifier, fd int, flags uint32) (*vfs.FileDescription, *syserr.Error) {
|
|
|
|
|
mnt := t.Kernel().SocketMount()
|
|
|
|
|
d := sockfs.NewDentry(t, mnt)
|
|
|
|
|
defer d.DecRef(t)
|
|
|
|
|
|
|
|
|
|
switch skType {
|
|
|
|
|
case syscall.SOCK_STREAM:
|
|
|
|
|
case syscall.SOCK_DGRAM:
|
|
|
|
|
default:
|
|
|
|
|
return nil, syserr.ErrSocketNotSupported
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
wq := &waiter.Queue{}
|
|
|
|
|
sop := &socketOperations{
|
|
|
|
|
family: family,
|
|
|
|
|
fd: uint32(fd),
|
|
|
|
|
protocol: protocol,
|
|
|
|
|
skType: skType,
|
|
|
|
|
eventInfo: plugin.EventInfo{Wq: wq},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sop.LockFD.Init(&vfs.FileLocks{})
|
|
|
|
|
|
|
|
|
|
vfsfd := &sop.vfsfd
|
|
|
|
|
if err := vfsfd.Init(sop, linux.O_RDWR|(flags&linux.O_NONBLOCK), mnt, d, &vfs.FileDescriptionOptions{
|
|
|
|
|
DenyPRead: true,
|
|
|
|
|
DenyPWrite: true,
|
|
|
|
|
UseDentryMetadata: true,
|
|
|
|
|
}); err != nil {
|
|
|
|
|
return nil, syserr.FromError(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
notifier.AddFD(uint32(fd), &sop.eventInfo)
|
|
|
|
|
|
|
|
|
|
return vfsfd, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Bind implements socket.Socket.Bind.
|
|
|
|
|
func (s *socketOperations) Bind(t *kernel.Task, sockaddr []byte) *syserr.Error {
|
|
|
|
|
return int2err(cgo.Bind(s.fd, sockaddr))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Listen implements socket.Socket.Listen.
|
|
|
|
|
func (s *socketOperations) Listen(t *kernel.Task, backlog int) *syserr.Error {
|
|
|
|
|
return int2err(cgo.Listen(s.fd, backlog))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Accept implements socket.Socket.Accept.
|
|
|
|
|
func (s *socketOperations) Accept(t *kernel.Task, peerRequested bool, flags int, blocking bool) (int32, linux.SockAddr, uint32, *syserr.Error) {
|
|
|
|
|
peerAddrBuf := make([]byte, sizeofSockaddr)
|
|
|
|
|
peerAddrlen := uint32(len(peerAddrBuf))
|
|
|
|
|
peerAddrPtr := &peerAddrBuf[0]
|
|
|
|
|
peerAddrlenPtr := &peerAddrlen
|
|
|
|
|
|
|
|
|
|
rc := cgo.Accept(s.fd, peerAddrPtr, peerAddrlenPtr)
|
|
|
|
|
if blocking {
|
|
|
|
|
for rc < 0 && (-rc) == errno.EAGAIN {
|
|
|
|
|
if err := s.waitEvent(t, waiter.EventIn); err != nil {
|
|
|
|
|
return 0, nil, 0, syserr.FromError(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rc = cgo.Accept(s.fd, peerAddrPtr, peerAddrlenPtr)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if rc < 0 {
|
|
|
|
|
return 0, nil, 0, int2err(rc)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
f, err := newSocket(t, s.family, s.skType, s.protocol, stack.notifier, int(rc), uint32(flags&syscall.SOCK_NONBLOCK))
|
|
|
|
|
if err != nil {
|
|
|
|
|
cgo.Close(uint32(rc))
|
|
|
|
|
return 0, nil, 0, err
|
|
|
|
|
}
|
|
|
|
|
defer f.DecRef(t)
|
|
|
|
|
|
|
|
|
|
kfd, kerr := t.NewFDFrom(0, f, kernel.FDFlags{
|
|
|
|
|
CloseOnExec: flags&syscall.SOCK_CLOEXEC != 0,
|
|
|
|
|
})
|
|
|
|
|
if kerr != nil {
|
|
|
|
|
cgo.Close(uint32(rc))
|
|
|
|
|
return 0, nil, 0, syserr.FromError(kerr)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
t.Kernel().RecordSocket(f)
|
|
|
|
|
|
|
|
|
|
if !peerRequested {
|
|
|
|
|
return kfd, nil, 0, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
peerAddr := socket.UnmarshalSockAddr(s.family, peerAddrBuf[:peerAddrlen])
|
|
|
|
|
return kfd, peerAddr, peerAddrlen, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Connect implements socket.Socket.Connect.
|
|
|
|
|
func (s *socketOperations) Connect(t *kernel.Task, sockaddr []byte, blocking bool) *syserr.Error {
|
|
|
|
|
var ret int64
|
|
|
|
|
|
|
|
|
|
if !blocking {
|
|
|
|
|
ret = cgo.Connect(s.fd, sockaddr)
|
|
|
|
|
|
|
|
|
|
if ret == 0 {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return int2err(ret)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = cgo.Connect(s.fd, sockaddr)
|
|
|
|
|
|
|
|
|
|
if ret == 0 {
|
|
|
|
|
return nil
|
|
|
|
|
} else if ret < 0 && (-ret) != errno.EINPROGRESS {
|
|
|
|
|
// Return error if errno is EALREADY/EISCONN/ECONNREFUSED/EINVAL
|
|
|
|
|
return int2err(ret)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := s.waitEvent(t, waiter.EventOut); err != nil {
|
|
|
|
|
return syserr.FromError(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Call connect() again after blocking to find connect's result.
|
|
|
|
|
ret = cgo.Connect(s.fd, sockaddr)
|
|
|
|
|
if ret == 0 {
|
|
|
|
|
return nil
|
|
|
|
|
} else if ret < 0 && (-ret) == errno.EISCONN {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return int2err(ret)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Shutdown implements socket.Socket.Shutdown.
|
|
|
|
|
func (s *socketOperations) Shutdown(t *kernel.Task, how int) *syserr.Error {
|
|
|
|
|
ret := cgo.Shutdown(s.fd, how)
|
|
|
|
|
return int2err(ret)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetSockOpt implements socket.Socket.GetSockOpt.
|
|
|
|
|
func (s *socketOperations) GetSockOpt(t *kernel.Task, level int, name int, outPtr hostarch.Addr, outLen int) (marshal.Marshallable, *syserr.Error) {
|
|
|
|
|
optVal := make([]byte, outLen)
|
|
|
|
|
if outLen == 0 {
|
|
|
|
|
optPtr := primitive.ByteSlice(optVal)
|
|
|
|
|
return &optPtr, nil
|
|
|
|
|
}
|
|
|
|
|
rc, outLen := cgo.Getsockopt(s.fd, level, name, optVal, outLen)
|
|
|
|
|
if rc < 0 {
|
|
|
|
|
return nil, int2err(rc)
|
|
|
|
|
}
|
|
|
|
|
optPtr := primitive.ByteSlice(optVal[:outLen])
|
|
|
|
|
return &optPtr, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SetSockOpt implements socket.Socket.SetSockOpt.
|
|
|
|
|
func (s *socketOperations) SetSockOpt(t *kernel.Task, level int, name int, optVal []byte) *syserr.Error {
|
|
|
|
|
rc := cgo.Setsockopt(s.fd, level, name, optVal)
|
|
|
|
|
return int2err(rc)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// State implements socket.Socket.State.
|
|
|
|
|
func (s *socketOperations) State() uint32 {
|
|
|
|
|
optVal := make([]byte, 1)
|
|
|
|
|
rc, _ := cgo.Getsockopt(s.fd, syscall.SOL_TCP, syscall.TCP_INFO, optVal, 1)
|
|
|
|
|
if rc < 0 {
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
return uint32(optVal[0])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Type implements socket.Socket.Type.
|
|
|
|
|
func (s *socketOperations) Type() (family int, skType linux.SockType, protocol int) {
|
|
|
|
|
return s.family, s.skType, s.protocol
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// OnClose implements vfs.FileDescriptionImpl.OnClose.
|
|
|
|
|
func (s *socketOperations) OnClose(ctx context.Context) error {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// EventRegister implements waiter.Waitable.EventRegister.
|
|
|
|
|
func (s *socketOperations) EventRegister(e *waiter.Entry) error {
|
|
|
|
|
s.eventInfo.Wq.EventRegister(e)
|
|
|
|
|
stack.notifier.UpdateFD(s.fd)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// EventUnregister implements waiter.Waitable.EventUnregister.
|
|
|
|
|
func (s *socketOperations) EventUnregister(e *waiter.Entry) {
|
|
|
|
|
s.eventInfo.Wq.EventUnregister(e)
|
|
|
|
|
stack.notifier.UpdateFD(s.fd)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Readiness implements socket.Socket.Readiness.
|
|
|
|
|
func (s *socketOperations) Readiness(mask waiter.EventMask) waiter.EventMask {
|
|
|
|
|
var events waiter.EventMask
|
|
|
|
|
|
|
|
|
|
evInfo := &s.eventInfo
|
|
|
|
|
iomask := mask & (waiter.EventIn | waiter.EventOut)
|
|
|
|
|
|
|
|
|
|
// Fast path condition:
|
|
|
|
|
// 1. The event needed has been reported by plugin stack io-thread; or
|
|
|
|
|
// 2. POLLIN or POLLOUT event has been reported by io-thread.
|
|
|
|
|
// Directly report current event without invoking cgo Readiness again.
|
|
|
|
|
if evInfo.Ready&iomask == iomask {
|
|
|
|
|
events = evInfo.Ready & mask
|
|
|
|
|
// Clear plugin stack eventInfo record after consuming IN/OUT event.
|
|
|
|
|
evInfo.Ready &= ^iomask
|
|
|
|
|
} else {
|
|
|
|
|
events = waiter.EventMask(cgo.Readiness(s.fd, uint64(mask)))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return events
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Epollable implements socket.Socket.Epollable.
|
|
|
|
|
func (s *socketOperations) Epollable() bool {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Refers to implementation in epsocket
|
|
|
|
|
func interfaceIoctl(ctx context.Context, io usermem.IO, cmd uint32, ifr *linux.IFReq) *syserr.Error {
|
|
|
|
|
stack := inet.StackFromContext(ctx)
|
|
|
|
|
if stack == nil {
|
|
|
|
|
return syserr.ErrNoDevice
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SIOCGIFNAME uses ifr.ifr_ifindex rather than ifr.ifr_name to
|
|
|
|
|
// identify a device.
|
|
|
|
|
if cmd == syscall.SIOCGIFNAME {
|
|
|
|
|
// Gets the name of the interface given the interface index
|
|
|
|
|
// stored in ifr_ifindex.
|
|
|
|
|
index := int32(hostarch.ByteOrder.Uint32(ifr.Data[:4]))
|
|
|
|
|
if iface, ok := stack.Interfaces()[index]; ok {
|
|
|
|
|
ifr.SetName(iface.Name)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return syserr.ErrNoDevice
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find the relevant device.
|
|
|
|
|
var iface inet.Interface
|
|
|
|
|
index := int32(-1)
|
|
|
|
|
for i, iface := range stack.Interfaces() {
|
|
|
|
|
if iface.Name == ifr.Name() {
|
|
|
|
|
index = i
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if index == -1 {
|
|
|
|
|
return syserr.ErrNoDevice
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch cmd {
|
|
|
|
|
case syscall.SIOCGIFINDEX:
|
|
|
|
|
// Copy out the index to the data.
|
|
|
|
|
hostarch.ByteOrder.PutUint32(ifr.Data[:], uint32(index))
|
|
|
|
|
|
|
|
|
|
case syscall.SIOCGIFHWADDR:
|
|
|
|
|
// Use IEEE802.3 instead of IEEE802.2 ARP type
|
|
|
|
|
// so that ifconfig command can recognize it
|
|
|
|
|
devType := 1
|
|
|
|
|
if index == ifLoIndex {
|
|
|
|
|
devType = 772 // Loopback
|
|
|
|
|
}
|
|
|
|
|
hostarch.ByteOrder.PutUint16(ifr.Data[:2], uint16(devType))
|
|
|
|
|
n := copy(ifr.Data[2:], iface.Addr)
|
|
|
|
|
for i := 2 + n; i < len(ifr.Data); i++ {
|
|
|
|
|
ifr.Data[i] = 0 // Clear padding.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case syscall.SIOCGIFFLAGS:
|
|
|
|
|
f := iface.Flags
|
|
|
|
|
// Drop the flags that don't fit in the size that we need to
|
|
|
|
|
// return. This matches Linux behavior.
|
|
|
|
|
hostarch.ByteOrder.PutUint16(ifr.Data[:2], uint16(f))
|
|
|
|
|
|
|
|
|
|
case syscall.SIOCGIFADDR:
|
|
|
|
|
// Copy the IPv4 address out.
|
|
|
|
|
for _, addr := range stack.InterfaceAddrs()[index] {
|
|
|
|
|
// This ioctl is only compatible with AF_INET addresses.
|
|
|
|
|
if addr.Family != linux.AF_INET {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
copyAddrOut(ifr, &addr)
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case syscall.SIOCGIFMETRIC:
|
|
|
|
|
// Gets the metric of the device. As per netdevice(7), this
|
|
|
|
|
// always just sets ifr_metric to 0.
|
|
|
|
|
hostarch.ByteOrder.PutUint32(ifr.Data[:4], 0)
|
|
|
|
|
|
|
|
|
|
case syscall.SIOCGIFMTU:
|
|
|
|
|
// Gets the MTU of the device.
|
|
|
|
|
hostarch.ByteOrder.PutUint32(ifr.Data[:4], iface.MTU)
|
|
|
|
|
|
|
|
|
|
case syscall.SIOCGIFMAP:
|
|
|
|
|
// Gets the hardware parameters of the device.
|
|
|
|
|
hostarch.ByteOrder.PutUint64(ifr.Data[:8], 0)
|
|
|
|
|
hostarch.ByteOrder.PutUint32(ifr.Data[8:12], 0)
|
|
|
|
|
ifr.Data[12] = 0
|
|
|
|
|
|
|
|
|
|
case syscall.SIOCGIFTXQLEN:
|
|
|
|
|
hostarch.ByteOrder.PutUint32(ifr.Data[:4], 1024)
|
|
|
|
|
|
|
|
|
|
case syscall.SIOCGIFDSTADDR:
|
|
|
|
|
// Gets the destination address of a point-to-point device.
|
|
|
|
|
// TODO: Implement SIOCGIFDSTADDR handler.
|
|
|
|
|
|
|
|
|
|
case syscall.SIOCGIFBRDADDR:
|
|
|
|
|
// Gets the broadcast address of a device.
|
|
|
|
|
// TODO: Implement SIOCGIFBRDADDR handler.
|
|
|
|
|
|
|
|
|
|
case syscall.SIOCGIFNETMASK:
|
|
|
|
|
// Gets the network mask of a device.
|
|
|
|
|
for _, addr := range stack.InterfaceAddrs()[index] {
|
|
|
|
|
// This ioctl is only compatible with AF_INET addresses.
|
|
|
|
|
if addr.Family != linux.AF_INET {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
// Populate ifr.ifr_netmask (type sockaddr).
|
|
|
|
|
hostarch.ByteOrder.PutUint16(ifr.Data[0:2], uint16(addr.Family))
|
|
|
|
|
hostarch.ByteOrder.PutUint16(ifr.Data[2:4], 0)
|
|
|
|
|
// Netmask is expected to be returned as a big endian value.
|
|
|
|
|
mask := uint32(0xffffffff << (32 - addr.PrefixLen))
|
|
|
|
|
binary.BigEndian.PutUint32(ifr.Data[4:8], mask)
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
// Not a valid call.
|
|
|
|
|
return syserr.ErrInvalidArgument
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func ifconfIoctlFromStack(ctx context.Context, io usermem.IO, ifc *linux.IFConf) error {
|
|
|
|
|
// If Ptr is NULL, return the necessary buffer size via Len.
|
|
|
|
|
// Otherwise, write up to Len bytes starting at Ptr containing ifreq
|
|
|
|
|
// structs.
|
|
|
|
|
t := ctx.(*kernel.Task)
|
|
|
|
|
s := t.NetworkContext().(*Stack)
|
|
|
|
|
if s == nil {
|
|
|
|
|
return syserr.ErrNoDevice.ToError()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ifc.Ptr == 0 {
|
|
|
|
|
ifc.Len = int32(len(s.Interfaces())) * int32(linux.SizeOfIFReq)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
max := ifc.Len
|
|
|
|
|
ifc.Len = 0
|
|
|
|
|
for idx, iface := range s.Interfaces() {
|
|
|
|
|
ifaceAddrs := s.InterfaceAddrs()[idx]
|
|
|
|
|
for _, ifaceAddr := range ifaceAddrs {
|
|
|
|
|
if ifaceAddr.Family != syscall.AF_INET {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Don't write past the end of the buffer.
|
|
|
|
|
if ifc.Len+int32(linux.SizeOfIFReq) > max {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Populate ifr.ifr_addr.
|
|
|
|
|
ifr := linux.IFReq{}
|
|
|
|
|
ifr.SetName(iface.Name)
|
|
|
|
|
copyAddrOut(&ifr, &ifaceAddr)
|
|
|
|
|
|
|
|
|
|
// Copy the ifr to userspace.
|
|
|
|
|
dst := uintptr(ifc.Ptr) + uintptr(ifc.Len)
|
|
|
|
|
ifc.Len += int32(linux.SizeOfIFReq)
|
|
|
|
|
if _, err := ifr.CopyOut(t, hostarch.Addr(dst)); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Ioctl implements socket.Socket.Ioctl.
|
|
|
|
|
func (s *socketOperations) Ioctl(ctx context.Context, io usermem.IO, sysno uintptr, args arch.SyscallArguments) (uintptr, error) {
|
|
|
|
|
cmd := uint32(args[1].Int())
|
|
|
|
|
arg := args[2].Pointer()
|
|
|
|
|
|
|
|
|
|
var buf []byte
|
|
|
|
|
switch cmd {
|
|
|
|
|
case syscall.SIOCGIFADDR,
|
|
|
|
|
syscall.SIOCGIFBRDADDR,
|
|
|
|
|
syscall.SIOCGIFDSTADDR,
|
|
|
|
|
syscall.SIOCGIFFLAGS,
|
|
|
|
|
syscall.SIOCGIFHWADDR,
|
|
|
|
|
syscall.SIOCGIFINDEX,
|
|
|
|
|
syscall.SIOCGIFMAP,
|
|
|
|
|
syscall.SIOCGIFMETRIC,
|
|
|
|
|
syscall.SIOCGIFMTU,
|
|
|
|
|
syscall.SIOCGIFNAME,
|
|
|
|
|
syscall.SIOCGIFNETMASK,
|
|
|
|
|
syscall.SIOCGIFTXQLEN:
|
|
|
|
|
var ifr linux.IFReq
|
|
|
|
|
ifrBuf := ctx.(*kernel.Task).CopyScratchBuffer(linux.SizeOfIFReq)
|
|
|
|
|
if _, err := io.CopyIn(ctx, args[2].Pointer(), ifrBuf, usermem.IOOpts{
|
|
|
|
|
AddressSpaceActive: true,
|
|
|
|
|
}); err != nil {
|
|
|
|
|
return 0, err
|
|
|
|
|
}
|
|
|
|
|
// Decode ifr from ifrBuf
|
|
|
|
|
// Note that these code may need to be modified if linux.IFReq struct changes
|
|
|
|
|
copy(ifr.IFName[0:linux.IFNAMSIZ], ifrBuf[0:linux.IFNAMSIZ])
|
|
|
|
|
copy(ifr.Data[0:linux.SizeOfIFReq-linux.IFNAMSIZ], ifrBuf[linux.IFNAMSIZ:linux.SizeOfIFReq])
|
|
|
|
|
if err := interfaceIoctl(ctx, io, cmd, &ifr); err != nil {
|
|
|
|
|
return 0, err.ToError()
|
|
|
|
|
}
|
|
|
|
|
copy(ifrBuf[0:linux.IFNAMSIZ], ifr.IFName[0:linux.IFNAMSIZ])
|
|
|
|
|
copy(ifrBuf[linux.IFNAMSIZ:linux.SizeOfIFReq], ifr.Data[0:linux.SizeOfIFReq-linux.IFNAMSIZ])
|
|
|
|
|
_, err := io.CopyOut(ctx, arg, ifrBuf, usermem.IOOpts{
|
|
|
|
|
AddressSpaceActive: true,
|
|
|
|
|
})
|
|
|
|
|
return 0, err
|
|
|
|
|
|
|
|
|
|
case syscall.SIOCGIFCONF:
|
|
|
|
|
// SIOCGIFCONF has slightly different behavior than the others, in that it
|
|
|
|
|
// will need to populate the array of ifreqs.
|
|
|
|
|
var ifc linux.IFConf
|
|
|
|
|
ifcBuf := ctx.(*kernel.Task).CopyScratchBuffer(linux.SizeOfIFConf)
|
|
|
|
|
if _, err := io.CopyIn(ctx, arg, ifcBuf, usermem.IOOpts{
|
|
|
|
|
AddressSpaceActive: true,
|
|
|
|
|
}); err != nil {
|
|
|
|
|
return 0, err
|
|
|
|
|
}
|
|
|
|
|
// Decode ifc from ifcBuf
|
|
|
|
|
// Note that these code may need to be modified if linux.IFconf struct changes
|
|
|
|
|
ifc.Len = int32(hostarch.ByteOrder.Uint32(ifcBuf[0:4]))
|
|
|
|
|
ifc.Ptr = hostarch.ByteOrder.Uint64(ifcBuf[8:])
|
|
|
|
|
|
|
|
|
|
if err := ifconfIoctlFromStack(ctx, io, &ifc); err != nil {
|
|
|
|
|
return 0, err
|
|
|
|
|
}
|
|
|
|
|
hostarch.ByteOrder.PutUint32(ifcBuf[0:4], uint32(ifc.Len))
|
|
|
|
|
hostarch.ByteOrder.PutUint64(ifcBuf[8:], ifc.Ptr)
|
|
|
|
|
_, err := io.CopyOut(ctx, arg, ifcBuf, usermem.IOOpts{
|
|
|
|
|
AddressSpaceActive: true,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return 0, err
|
|
|
|
|
case linux.SIOCGIFMEM, linux.SIOCGIFPFLAGS, linux.SIOCGMIIPHY, linux.SIOCGMIIREG:
|
|
|
|
|
unimpl.EmitUnimplementedEvent(ctx, sysno)
|
|
|
|
|
return 0, linuxerr.ENOTTY
|
|
|
|
|
case syscall.TIOCINQ, syscall.TIOCOUTQ:
|
|
|
|
|
buf = make([]byte, 4)
|
|
|
|
|
case syscall.SIOCGSTAMP:
|
|
|
|
|
buf = make([]byte, 16)
|
|
|
|
|
default:
|
|
|
|
|
return 0, linuxerr.ENOTTY
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rc := cgo.Ioctl(s.fd, cmd, buf)
|
|
|
|
|
if rc < 0 {
|
|
|
|
|
_, err := translateReturn(int64(rc))
|
|
|
|
|
return 0, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err := io.CopyOut(ctx, arg, buf, usermem.IOOpts{
|
|
|
|
|
AddressSpaceActive: true,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return 0, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Release implements socket.Socket.Release.
|
|
|
|
|
func (s *socketOperations) Release(ctx context.Context) {
|
|
|
|
|
t := kernel.TaskFromContext(ctx)
|
|
|
|
|
t.Kernel().DeleteSocket(&s.vfsfd)
|
|
|
|
|
stack.notifier.RemoveFD(s.fd)
|
|
|
|
|
cgo.Close(s.fd)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetSockName implements socket.Socket.GetSockName.
|
|
|
|
|
func (s *socketOperations) GetSockName(t *kernel.Task) (linux.SockAddr, uint32, *syserr.Error) {
|
|
|
|
|
addrlen := uint32(sizeofSockaddr)
|
|
|
|
|
addr := make([]byte, sizeofSockaddr)
|
|
|
|
|
rc := cgo.Getsockname(s.fd, addr, &addrlen)
|
|
|
|
|
if rc < 0 {
|
|
|
|
|
return nil, 0, int2err(rc)
|
|
|
|
|
}
|
|
|
|
|
return socket.UnmarshalSockAddr(s.family, addr), addrlen, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetPeerName implements socket.Socket.GetPeerName.
|
|
|
|
|
func (s *socketOperations) GetPeerName(t *kernel.Task) (linux.SockAddr, uint32, *syserr.Error) {
|
|
|
|
|
addrlen := uint32(sizeofSockaddr)
|
|
|
|
|
addr := make([]byte, sizeofSockaddr)
|
|
|
|
|
rc := cgo.GetPeername(s.fd, addr, &addrlen)
|
|
|
|
|
if rc < 0 {
|
|
|
|
|
return nil, 0, int2err(rc)
|
|
|
|
|
}
|
|
|
|
|
return socket.UnmarshalSockAddr(s.family, addr), addrlen, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// recv is a helper function for doing non-blocking read once.
|
|
|
|
|
// It returns:
|
|
|
|
|
// 1. number of bytes received;
|
|
|
|
|
// 2. sender address for non-stream socket;
|
|
|
|
|
// 3. control message;
|
|
|
|
|
// 4. message flag;
|
|
|
|
|
// 5. internal error if any
|
|
|
|
|
func (s *socketOperations) recv(t *kernel.Task, dst usermem.IOSequence, sysflags int, addr []byte, control []byte) (int64, []byte, []byte, int, error) {
|
|
|
|
|
bytes := dst.NumBytes()
|
|
|
|
|
// Directly return if dst is empty.
|
|
|
|
|
if bytes == 0 {
|
|
|
|
|
return 0, addr, control, 0, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Slow path for non-stream socket(e.g., UDP, raw socket).
|
|
|
|
|
if s.skType != linux.SOCK_STREAM {
|
|
|
|
|
if len(addr) == 0 && s.skType == linux.SOCK_DGRAM {
|
|
|
|
|
addr = make([]byte, sizeofSockaddr)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tmpBuf := make([]byte, bytes)
|
|
|
|
|
tmpBS := safemem.BlockSeqOf(safemem.BlockFromSafeSlice(tmpBuf))
|
|
|
|
|
iovs := iovecsFromBlockSeq(tmpBS, nil)
|
|
|
|
|
rc, la, lc, flag := cgo.Recvmsg(s.fd, iovs, addr, control, sysflags)
|
|
|
|
|
if rc < 0 {
|
|
|
|
|
_, err := translateReturn(rc)
|
|
|
|
|
return 0, addr /* return the original slice */, control /* return the original slice */, 0, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
n, err := dst.CopyOut(t, tmpBuf[:rc])
|
|
|
|
|
return int64(n), addr[:la], control[:lc], flag, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fast path for stream socket(e.g., TCP socket).
|
|
|
|
|
rw := getReadWriter(s.fd)
|
|
|
|
|
rw.to = control
|
|
|
|
|
rw.flags = uint32(sysflags)
|
|
|
|
|
|
|
|
|
|
n, err := dst.CopyOutFrom(t, rw)
|
|
|
|
|
|
|
|
|
|
control = rw.to
|
2024-09-23 14:12:32 -07:00
|
|
|
msgFlags := int(rw.flags)
|
2023-10-11 14:44:29 +08:00
|
|
|
putReadWriter(rw)
|
|
|
|
|
|
2024-09-23 14:12:32 -07:00
|
|
|
return n, nil /* ignore for TCP */, control, msgFlags, err
|
2023-10-11 14:44:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// RecvMsg implements socket.Socket.RecvMsg.
|
|
|
|
|
func (s *socketOperations) RecvMsg(t *kernel.Task, dst usermem.IOSequence, flags int, haveDeadline bool, deadline ktime.Time, senderRequested bool, controlDataLen uint64) (int, int, linux.SockAddr, uint32, socket.ControlMessages, *syserr.Error) {
|
|
|
|
|
var addr, control []byte
|
|
|
|
|
if senderRequested {
|
|
|
|
|
if s.skType != linux.SOCK_STREAM {
|
|
|
|
|
addr = make([]byte, sizeofSockaddr)
|
|
|
|
|
} else {
|
|
|
|
|
// According to UNIX98, msg_name/msg_namelen are ignored on connected socket.
|
|
|
|
|
senderRequested = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if controlDataLen > 0 {
|
|
|
|
|
control = make([]byte, controlDataLen)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Store nonblock info from original flag.
|
|
|
|
|
nonblock := flags&syscall.MSG_DONTWAIT != 0
|
|
|
|
|
waitall := nonblock == false && flags&syscall.MSG_WAITALL != 0
|
|
|
|
|
|
|
|
|
|
var mflag int
|
|
|
|
|
var n int64
|
|
|
|
|
var err error
|
|
|
|
|
// Always set as non-blocking for recv.
|
|
|
|
|
sysflags := flags | syscall.MSG_DONTWAIT
|
|
|
|
|
n, addr, control, mflag, err = s.recv(t, dst, sysflags, addr, control)
|
|
|
|
|
|
|
|
|
|
var senderAddr linux.SockAddr
|
|
|
|
|
var addrlen uint32
|
|
|
|
|
// For non-blocking RecvMsg, we could return after recv once.
|
|
|
|
|
if nonblock {
|
|
|
|
|
if err != nil {
|
|
|
|
|
// Clear controlMessages when error occurs.
|
|
|
|
|
control = nil
|
|
|
|
|
} else if senderRequested && len(addr) > 0 {
|
|
|
|
|
senderAddr = socket.UnmarshalSockAddr(s.family, addr)
|
|
|
|
|
addrlen = uint32(len(addr))
|
|
|
|
|
}
|
|
|
|
|
controlMessages := buildControlMessage(control)
|
|
|
|
|
// The socket is shutdown, report ErrWouldBlock for non-blocking mode.
|
|
|
|
|
if err == error(syscall.ESHUTDOWN) {
|
|
|
|
|
err = linuxerr.ErrWouldBlock
|
|
|
|
|
}
|
|
|
|
|
return int(n), mflag, senderAddr, addrlen, *controlMessages, syserr.FromError(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// For blocking RecvMsg, we need to finish reading all data before return.
|
|
|
|
|
rn := n
|
|
|
|
|
for err == linuxerr.ErrWouldBlock || (waitall && err == nil && rn < dst.NumBytes()) {
|
|
|
|
|
dst = dst.DropFirst(int(rn))
|
|
|
|
|
// Wait on POLLIN event.
|
|
|
|
|
if haveDeadline {
|
|
|
|
|
err = s.waitEventT(t, waiter.EventIn, deadline)
|
|
|
|
|
} else {
|
|
|
|
|
err = s.waitEvent(t, waiter.EventIn)
|
|
|
|
|
}
|
|
|
|
|
if err != nil {
|
|
|
|
|
if n == 0 {
|
|
|
|
|
// There is no POLLIN event reported before timeout.
|
|
|
|
|
if err == linuxerr.ETIMEDOUT {
|
|
|
|
|
err = linuxerr.ErrWouldBlock
|
|
|
|
|
}
|
|
|
|
|
return 0, 0, nil, 0, socket.ControlMessages{}, syserr.FromError(err)
|
|
|
|
|
}
|
|
|
|
|
// Clear event-waiting error.
|
|
|
|
|
err = nil
|
|
|
|
|
mflag = 0
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
rn, addr, control, mflag, err = s.recv(t, dst, sysflags, addr, control)
|
|
|
|
|
n += rn
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Plugin stack finds the socket has been shutdown,
|
|
|
|
|
// report ESHUTDOWN through controlMessages.
|
|
|
|
|
if err == error(syscall.ESHUTDOWN) {
|
|
|
|
|
n = 0
|
|
|
|
|
err = nil
|
|
|
|
|
} else if n > 0 {
|
|
|
|
|
// If any data has been received, clear internal error.
|
|
|
|
|
err = nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// For receiving error other than ESHUTDOWN and not receiving any data,
|
|
|
|
|
// clear controlMessages and report through internal error.
|
|
|
|
|
if err != nil {
|
|
|
|
|
control = nil
|
|
|
|
|
} else if senderRequested && len(addr) > 0 {
|
|
|
|
|
senderAddr = socket.UnmarshalSockAddr(s.family, addr)
|
|
|
|
|
addrlen = uint32(len(addr))
|
|
|
|
|
}
|
|
|
|
|
controlMessages := buildControlMessage(control)
|
|
|
|
|
return int(n), mflag, senderAddr, addrlen, *controlMessages, syserr.FromError(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Read implements socket.Socket.Read.
|
|
|
|
|
func (s *socketOperations) Read(ctx context.Context, dst usermem.IOSequence, opts vfs.ReadOptions) (int64, error) {
|
|
|
|
|
if opts.Flags != 0 {
|
|
|
|
|
return 0, linuxerr.EOPNOTSUPP
|
|
|
|
|
}
|
|
|
|
|
nonblock := (s.vfsfd.StatusFlags() & linux.O_NONBLOCK) != 0
|
|
|
|
|
|
|
|
|
|
bytes := dst.NumBytes()
|
|
|
|
|
// Directly return if dst is empty.
|
|
|
|
|
if bytes == 0 {
|
|
|
|
|
return 0, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rw := getReadWriter(s.fd)
|
|
|
|
|
n, err := dst.CopyOutFrom(ctx, rw)
|
|
|
|
|
putReadWriter(rw)
|
|
|
|
|
|
|
|
|
|
if err == error(syscall.ESHUTDOWN) {
|
|
|
|
|
if nonblock {
|
|
|
|
|
err = linuxerr.ErrWouldBlock
|
|
|
|
|
} else {
|
|
|
|
|
n = 0
|
|
|
|
|
err = nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return int64(n), err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *socketOperations) send(t *kernel.Task, src usermem.IOSequence, to []byte, sysflags int) (int64, error) {
|
|
|
|
|
bytes := src.NumBytes()
|
|
|
|
|
// If src is empty, let plugin stack handle sending 0-byte data based on protocol.
|
|
|
|
|
if bytes == 0 {
|
|
|
|
|
rc := cgo.Sendto(s.fd, 0, 0, sysflags, to)
|
|
|
|
|
ret, err := translateReturn(rc)
|
|
|
|
|
return int64(ret), err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rw := getReadWriter(s.fd)
|
|
|
|
|
rw.to = to
|
|
|
|
|
n, err := src.CopyInTo(t, rw)
|
|
|
|
|
putReadWriter(rw)
|
|
|
|
|
return n, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SendMsg implements socket.Socket.SendMsg.
|
|
|
|
|
func (s *socketOperations) SendMsg(t *kernel.Task, src usermem.IOSequence, to []byte, flags int, haveDeadline bool, deadline ktime.Time, controlMessages socket.ControlMessages) (int, *syserr.Error) {
|
|
|
|
|
total := src.NumBytes()
|
|
|
|
|
nonblock := flags & syscall.MSG_DONTWAIT
|
|
|
|
|
sysflags := flags | syscall.MSG_DONTWAIT
|
|
|
|
|
|
|
|
|
|
n, err := s.send(t, src, to, sysflags)
|
|
|
|
|
|
|
|
|
|
if nonblock != 0 {
|
|
|
|
|
return int(n), syserr.FromError(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var sent int64
|
|
|
|
|
for err == nil || err == linuxerr.ErrWouldBlock {
|
|
|
|
|
if n > 0 {
|
|
|
|
|
src = src.DropFirst64(n)
|
|
|
|
|
sent += n
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if sent == total {
|
|
|
|
|
return int(total), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if haveDeadline {
|
|
|
|
|
err = s.waitEventT(t, waiter.EventOut, deadline)
|
|
|
|
|
} else {
|
|
|
|
|
err = s.waitEvent(t, waiter.EventOut)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
if err == linuxerr.ETIMEDOUT {
|
|
|
|
|
err = linuxerr.ErrWouldBlock
|
|
|
|
|
}
|
|
|
|
|
return int(sent), syserr.FromError(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
n, err = s.send(t, src, to, sysflags)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return int(sent), syserr.FromError(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Write implements socket.Socket.Write.
|
|
|
|
|
func (s *socketOperations) Write(ctx context.Context, src usermem.IOSequence, opts vfs.WriteOptions) (int64, error) {
|
|
|
|
|
bytes := src.NumBytes()
|
|
|
|
|
// If src is empty, let plugin stack handle sending 0-byte data based on protocol.
|
|
|
|
|
if bytes == 0 {
|
|
|
|
|
rc := cgo.Write(s.fd, 0, 0)
|
|
|
|
|
ret, err := translateReturn(rc)
|
|
|
|
|
return int64(ret), err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rw := getReadWriter(s.fd)
|
|
|
|
|
n, err := src.CopyInTo(ctx, rw)
|
|
|
|
|
putReadWriter(rw)
|
|
|
|
|
if n < bytes && err == nil {
|
|
|
|
|
return n, linuxerr.ErrWouldBlock
|
|
|
|
|
}
|
|
|
|
|
return int64(n), err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// waitEvent implements blocking wait on given events
|
|
|
|
|
// until one of the events have been reported.
|
|
|
|
|
func (s *socketOperations) waitEvent(ctx context.Context, event waiter.EventMask) error {
|
|
|
|
|
var err error
|
|
|
|
|
t := ctx.(*kernel.Task)
|
|
|
|
|
e, ch := waiter.NewChannelEntry(event | waiter.EventErr | waiter.EventHUp)
|
|
|
|
|
s.EventRegister(&e)
|
|
|
|
|
|
|
|
|
|
// It's possible events happens between last check and EventRegister.
|
|
|
|
|
// If this happens and we don't check readiness again, we would miss
|
|
|
|
|
// the event and get blocked forever.
|
|
|
|
|
if s.Readiness(event|waiter.EventErr|waiter.EventHUp) == 0 {
|
|
|
|
|
err = t.Block(ch)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s.EventUnregister(&e)
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// waitEventT implements blocking wait on given events
|
|
|
|
|
// until one of the events have been reported or timeout based on given deadline.
|
|
|
|
|
func (s *socketOperations) waitEventT(ctx context.Context, event waiter.EventMask, deadline ktime.Time) error {
|
|
|
|
|
var err error
|
|
|
|
|
t := ctx.(*kernel.Task)
|
|
|
|
|
e, ch := waiter.NewChannelEntry(event | waiter.EventErr | waiter.EventHUp)
|
|
|
|
|
s.EventRegister(&e)
|
|
|
|
|
|
|
|
|
|
if s.Readiness(event|waiter.EventErr|waiter.EventHUp) == 0 {
|
|
|
|
|
err = t.BlockWithDeadline(ch, true, deadline)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s.EventUnregister(&e)
|
|
|
|
|
return err
|
|
|
|
|
}
|