mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Port netstack, hostinet, and netlink sockets to VFS2.
All three follow the same pattern: 1. Refactor VFS1 sockets into socketOpsCommon, so that most of the methods can be shared with VFS2. 2. Create a FileDescriptionImpl with the corresponding socket operations, rewriting the few that cannot be shared with VFS1. 3. Set up a VFS2 socket provider that creates a socket by setting up a dentry in the global Kernel.socketMount and connecting it with a new FileDescription. This mostly completes the work for porting sockets to VFS2, and many syscall tests can be enabled as a result. There are several networking-related syscall tests that are still not passing: 1. net gofer tests 2. socketpair gofer tests 2. sendfile tests (splice is not implemented in VFS2 yet) Updates #1478, #1484, #1485 PiperOrigin-RevId: 309457331
This commit is contained in:
@@ -7,7 +7,9 @@ go_library(
|
||||
srcs = [
|
||||
"device.go",
|
||||
"netstack.go",
|
||||
"netstack_vfs2.go",
|
||||
"provider.go",
|
||||
"provider_vfs2.go",
|
||||
"save_restore.go",
|
||||
"stack.go",
|
||||
],
|
||||
@@ -25,6 +27,8 @@ go_library(
|
||||
"//pkg/sentry/device",
|
||||
"//pkg/sentry/fs",
|
||||
"//pkg/sentry/fs/fsutil",
|
||||
"//pkg/sentry/fsimpl/kernfs",
|
||||
"//pkg/sentry/fsimpl/sockfs",
|
||||
"//pkg/sentry/inet",
|
||||
"//pkg/sentry/kernel",
|
||||
"//pkg/sentry/kernel/auth",
|
||||
@@ -32,6 +36,7 @@ go_library(
|
||||
"//pkg/sentry/socket",
|
||||
"//pkg/sentry/socket/netfilter",
|
||||
"//pkg/sentry/unimpl",
|
||||
"//pkg/sentry/vfs",
|
||||
"//pkg/sync",
|
||||
"//pkg/syserr",
|
||||
"//pkg/syserror",
|
||||
|
||||
@@ -252,6 +252,8 @@ type commonEndpoint interface {
|
||||
GetSockOptInt(opt tcpip.SockOptInt) (int, *tcpip.Error)
|
||||
}
|
||||
|
||||
// LINT.IfChange
|
||||
|
||||
// SocketOperations encapsulates all the state needed to represent a network stack
|
||||
// endpoint in the kernel context.
|
||||
//
|
||||
@@ -263,6 +265,14 @@ type SocketOperations struct {
|
||||
fsutil.FileNoFsync `state:"nosave"`
|
||||
fsutil.FileNoMMap `state:"nosave"`
|
||||
fsutil.FileUseInodeUnstableAttr `state:"nosave"`
|
||||
|
||||
socketOpsCommon
|
||||
}
|
||||
|
||||
// socketOpsCommon contains the socket operations common to VFS1 and VFS2.
|
||||
//
|
||||
// +stateify savable
|
||||
type socketOpsCommon struct {
|
||||
socket.SendReceiveTimeout
|
||||
*waiter.Queue
|
||||
|
||||
@@ -314,11 +324,13 @@ func New(t *kernel.Task, family int, skType linux.SockType, protocol int, queue
|
||||
dirent := socket.NewDirent(t, netstackDevice)
|
||||
defer dirent.DecRef()
|
||||
return fs.NewFile(t, dirent, fs.FileFlags{Read: true, Write: true, NonSeekable: true}, &SocketOperations{
|
||||
Queue: queue,
|
||||
family: family,
|
||||
Endpoint: endpoint,
|
||||
skType: skType,
|
||||
protocol: protocol,
|
||||
socketOpsCommon: socketOpsCommon{
|
||||
Queue: queue,
|
||||
family: family,
|
||||
Endpoint: endpoint,
|
||||
skType: skType,
|
||||
protocol: protocol,
|
||||
},
|
||||
}), nil
|
||||
}
|
||||
|
||||
@@ -417,7 +429,7 @@ func AddressAndFamily(addr []byte) (tcpip.FullAddress, uint16, *syserr.Error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SocketOperations) isPacketBased() bool {
|
||||
func (s *socketOpsCommon) isPacketBased() bool {
|
||||
return s.skType == linux.SOCK_DGRAM || s.skType == linux.SOCK_SEQPACKET || s.skType == linux.SOCK_RDM || s.skType == linux.SOCK_RAW
|
||||
}
|
||||
|
||||
@@ -425,7 +437,7 @@ func (s *SocketOperations) isPacketBased() bool {
|
||||
// empty. It assumes that the socket is locked.
|
||||
//
|
||||
// Precondition: s.readMu must be held.
|
||||
func (s *SocketOperations) fetchReadView() *syserr.Error {
|
||||
func (s *socketOpsCommon) fetchReadView() *syserr.Error {
|
||||
if len(s.readView) > 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -446,7 +458,7 @@ func (s *SocketOperations) fetchReadView() *syserr.Error {
|
||||
}
|
||||
|
||||
// Release implements fs.FileOperations.Release.
|
||||
func (s *SocketOperations) Release() {
|
||||
func (s *socketOpsCommon) Release() {
|
||||
s.Endpoint.Close()
|
||||
}
|
||||
|
||||
@@ -633,7 +645,7 @@ func (s *SocketOperations) ReadFrom(ctx context.Context, _ *fs.File, r io.Reader
|
||||
}
|
||||
|
||||
// Readiness returns a mask of ready events for socket s.
|
||||
func (s *SocketOperations) Readiness(mask waiter.EventMask) waiter.EventMask {
|
||||
func (s *socketOpsCommon) Readiness(mask waiter.EventMask) waiter.EventMask {
|
||||
r := s.Endpoint.Readiness(mask)
|
||||
|
||||
// Check our cached value iff the caller asked for readability and the
|
||||
@@ -647,7 +659,7 @@ func (s *SocketOperations) Readiness(mask waiter.EventMask) waiter.EventMask {
|
||||
return r
|
||||
}
|
||||
|
||||
func (s *SocketOperations) checkFamily(family uint16, exact bool) *syserr.Error {
|
||||
func (s *socketOpsCommon) checkFamily(family uint16, exact bool) *syserr.Error {
|
||||
if family == uint16(s.family) {
|
||||
return nil
|
||||
}
|
||||
@@ -670,7 +682,7 @@ func (s *SocketOperations) checkFamily(family uint16, exact bool) *syserr.Error
|
||||
// represented by the empty string.
|
||||
//
|
||||
// TODO(gvisor.dev/issue/1556): remove this function.
|
||||
func (s *SocketOperations) mapFamily(addr tcpip.FullAddress, family uint16) tcpip.FullAddress {
|
||||
func (s *socketOpsCommon) mapFamily(addr tcpip.FullAddress, family uint16) tcpip.FullAddress {
|
||||
if len(addr.Addr) == 0 && s.family == linux.AF_INET6 && family == linux.AF_INET {
|
||||
addr.Addr = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00"
|
||||
}
|
||||
@@ -679,7 +691,7 @@ func (s *SocketOperations) mapFamily(addr tcpip.FullAddress, family uint16) tcpi
|
||||
|
||||
// Connect implements the linux syscall connect(2) for sockets backed by
|
||||
// tpcip.Endpoint.
|
||||
func (s *SocketOperations) Connect(t *kernel.Task, sockaddr []byte, blocking bool) *syserr.Error {
|
||||
func (s *socketOpsCommon) Connect(t *kernel.Task, sockaddr []byte, blocking bool) *syserr.Error {
|
||||
addr, family, err := AddressAndFamily(sockaddr)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -725,7 +737,7 @@ func (s *SocketOperations) Connect(t *kernel.Task, sockaddr []byte, blocking boo
|
||||
|
||||
// Bind implements the linux syscall bind(2) for sockets backed by
|
||||
// tcpip.Endpoint.
|
||||
func (s *SocketOperations) Bind(t *kernel.Task, sockaddr []byte) *syserr.Error {
|
||||
func (s *socketOpsCommon) Bind(t *kernel.Task, sockaddr []byte) *syserr.Error {
|
||||
if len(sockaddr) < 2 {
|
||||
return syserr.ErrInvalidArgument
|
||||
}
|
||||
@@ -771,13 +783,13 @@ func (s *SocketOperations) Bind(t *kernel.Task, sockaddr []byte) *syserr.Error {
|
||||
|
||||
// Listen implements the linux syscall listen(2) for sockets backed by
|
||||
// tcpip.Endpoint.
|
||||
func (s *SocketOperations) Listen(t *kernel.Task, backlog int) *syserr.Error {
|
||||
func (s *socketOpsCommon) Listen(t *kernel.Task, backlog int) *syserr.Error {
|
||||
return syserr.TranslateNetstackError(s.Endpoint.Listen(backlog))
|
||||
}
|
||||
|
||||
// blockingAccept implements a blocking version of accept(2), that is, if no
|
||||
// connections are ready to be accept, it will block until one becomes ready.
|
||||
func (s *SocketOperations) blockingAccept(t *kernel.Task) (tcpip.Endpoint, *waiter.Queue, *syserr.Error) {
|
||||
func (s *socketOpsCommon) blockingAccept(t *kernel.Task) (tcpip.Endpoint, *waiter.Queue, *syserr.Error) {
|
||||
// Register for notifications.
|
||||
e, ch := waiter.NewChannelEntry(nil)
|
||||
s.EventRegister(&e, waiter.EventIn)
|
||||
@@ -863,7 +875,7 @@ func ConvertShutdown(how int) (tcpip.ShutdownFlags, *syserr.Error) {
|
||||
|
||||
// Shutdown implements the linux syscall shutdown(2) for sockets backed by
|
||||
// tcpip.Endpoint.
|
||||
func (s *SocketOperations) Shutdown(t *kernel.Task, how int) *syserr.Error {
|
||||
func (s *socketOpsCommon) Shutdown(t *kernel.Task, how int) *syserr.Error {
|
||||
f, err := ConvertShutdown(how)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -2258,7 +2270,7 @@ func ConvertAddress(family int, addr tcpip.FullAddress) (linux.SockAddr, uint32)
|
||||
|
||||
// GetSockName implements the linux syscall getsockname(2) for sockets backed by
|
||||
// tcpip.Endpoint.
|
||||
func (s *SocketOperations) GetSockName(t *kernel.Task) (linux.SockAddr, uint32, *syserr.Error) {
|
||||
func (s *socketOpsCommon) GetSockName(t *kernel.Task) (linux.SockAddr, uint32, *syserr.Error) {
|
||||
addr, err := s.Endpoint.GetLocalAddress()
|
||||
if err != nil {
|
||||
return nil, 0, syserr.TranslateNetstackError(err)
|
||||
@@ -2270,7 +2282,7 @@ func (s *SocketOperations) GetSockName(t *kernel.Task) (linux.SockAddr, uint32,
|
||||
|
||||
// GetPeerName implements the linux syscall getpeername(2) for sockets backed by
|
||||
// tcpip.Endpoint.
|
||||
func (s *SocketOperations) GetPeerName(t *kernel.Task) (linux.SockAddr, uint32, *syserr.Error) {
|
||||
func (s *socketOpsCommon) GetPeerName(t *kernel.Task) (linux.SockAddr, uint32, *syserr.Error) {
|
||||
addr, err := s.Endpoint.GetRemoteAddress()
|
||||
if err != nil {
|
||||
return nil, 0, syserr.TranslateNetstackError(err)
|
||||
@@ -2285,7 +2297,7 @@ func (s *SocketOperations) GetPeerName(t *kernel.Task) (linux.SockAddr, uint32,
|
||||
// caller.
|
||||
//
|
||||
// Precondition: s.readMu must be locked.
|
||||
func (s *SocketOperations) coalescingRead(ctx context.Context, dst usermem.IOSequence, discard bool) (int, *syserr.Error) {
|
||||
func (s *socketOpsCommon) coalescingRead(ctx context.Context, dst usermem.IOSequence, discard bool) (int, *syserr.Error) {
|
||||
var err *syserr.Error
|
||||
var copied int
|
||||
|
||||
@@ -2337,7 +2349,7 @@ func (s *SocketOperations) coalescingRead(ctx context.Context, dst usermem.IOSeq
|
||||
return 0, err
|
||||
}
|
||||
|
||||
func (s *SocketOperations) fillCmsgInq(cmsg *socket.ControlMessages) {
|
||||
func (s *socketOpsCommon) fillCmsgInq(cmsg *socket.ControlMessages) {
|
||||
if !s.sockOptInq {
|
||||
return
|
||||
}
|
||||
@@ -2352,7 +2364,7 @@ func (s *SocketOperations) fillCmsgInq(cmsg *socket.ControlMessages) {
|
||||
// nonBlockingRead issues a non-blocking read.
|
||||
//
|
||||
// TODO(b/78348848): Support timestamps for stream sockets.
|
||||
func (s *SocketOperations) nonBlockingRead(ctx context.Context, dst usermem.IOSequence, peek, trunc, senderRequested bool) (int, int, linux.SockAddr, uint32, socket.ControlMessages, *syserr.Error) {
|
||||
func (s *socketOpsCommon) nonBlockingRead(ctx context.Context, dst usermem.IOSequence, peek, trunc, senderRequested bool) (int, int, linux.SockAddr, uint32, socket.ControlMessages, *syserr.Error) {
|
||||
isPacket := s.isPacketBased()
|
||||
|
||||
// Fast path for regular reads from stream (e.g., TCP) endpoints. Note
|
||||
@@ -2461,7 +2473,7 @@ func (s *SocketOperations) nonBlockingRead(ctx context.Context, dst usermem.IOSe
|
||||
return n, flags, addr, addrLen, cmsg, syserr.FromError(err)
|
||||
}
|
||||
|
||||
func (s *SocketOperations) controlMessages() socket.ControlMessages {
|
||||
func (s *socketOpsCommon) controlMessages() socket.ControlMessages {
|
||||
return socket.ControlMessages{
|
||||
IP: tcpip.ControlMessages{
|
||||
HasTimestamp: s.readCM.HasTimestamp && s.sockOptTimestamp,
|
||||
@@ -2480,7 +2492,7 @@ func (s *SocketOperations) controlMessages() socket.ControlMessages {
|
||||
// successfully writing packet data out to userspace.
|
||||
//
|
||||
// Precondition: s.readMu must be locked.
|
||||
func (s *SocketOperations) updateTimestamp() {
|
||||
func (s *socketOpsCommon) updateTimestamp() {
|
||||
// Save the SIOCGSTAMP timestamp only if SO_TIMESTAMP is disabled.
|
||||
if !s.sockOptTimestamp {
|
||||
s.timestampValid = true
|
||||
@@ -2490,7 +2502,7 @@ func (s *SocketOperations) updateTimestamp() {
|
||||
|
||||
// RecvMsg implements the linux syscall recvmsg(2) for sockets backed by
|
||||
// tcpip.Endpoint.
|
||||
func (s *SocketOperations) RecvMsg(t *kernel.Task, dst usermem.IOSequence, flags int, haveDeadline bool, deadline ktime.Time, senderRequested bool, controlDataLen uint64) (n int, msgFlags int, senderAddr linux.SockAddr, senderAddrLen uint32, controlMessages socket.ControlMessages, err *syserr.Error) {
|
||||
func (s *socketOpsCommon) RecvMsg(t *kernel.Task, dst usermem.IOSequence, flags int, haveDeadline bool, deadline ktime.Time, senderRequested bool, controlDataLen uint64) (n int, msgFlags int, senderAddr linux.SockAddr, senderAddrLen uint32, controlMessages socket.ControlMessages, err *syserr.Error) {
|
||||
trunc := flags&linux.MSG_TRUNC != 0
|
||||
peek := flags&linux.MSG_PEEK != 0
|
||||
dontWait := flags&linux.MSG_DONTWAIT != 0
|
||||
@@ -2558,7 +2570,7 @@ func (s *SocketOperations) RecvMsg(t *kernel.Task, dst usermem.IOSequence, flags
|
||||
|
||||
// SendMsg implements the linux syscall sendmsg(2) for sockets backed by
|
||||
// tcpip.Endpoint.
|
||||
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) {
|
||||
func (s *socketOpsCommon) SendMsg(t *kernel.Task, src usermem.IOSequence, to []byte, flags int, haveDeadline bool, deadline ktime.Time, controlMessages socket.ControlMessages) (int, *syserr.Error) {
|
||||
// Reject Unix control messages.
|
||||
if !controlMessages.Unix.Empty() {
|
||||
return 0, syserr.ErrInvalidArgument
|
||||
@@ -2634,6 +2646,10 @@ func (s *SocketOperations) SendMsg(t *kernel.Task, src usermem.IOSequence, to []
|
||||
|
||||
// Ioctl implements fs.FileOperations.Ioctl.
|
||||
func (s *SocketOperations) Ioctl(ctx context.Context, _ *fs.File, io usermem.IO, args arch.SyscallArguments) (uintptr, error) {
|
||||
return s.socketOpsCommon.ioctl(ctx, io, args)
|
||||
}
|
||||
|
||||
func (s *socketOpsCommon) ioctl(ctx context.Context, io usermem.IO, args arch.SyscallArguments) (uintptr, error) {
|
||||
// SIOCGSTAMP is implemented by netstack rather than all commonEndpoint
|
||||
// sockets.
|
||||
// TODO(b/78348848): Add a commonEndpoint method to support SIOCGSTAMP.
|
||||
@@ -2973,7 +2989,7 @@ func nicStateFlagsToLinux(f stack.NICStateFlags) uint32 {
|
||||
|
||||
// State implements socket.Socket.State. State translates the internal state
|
||||
// returned by netstack to values defined by Linux.
|
||||
func (s *SocketOperations) State() uint32 {
|
||||
func (s *socketOpsCommon) State() uint32 {
|
||||
if s.family != linux.AF_INET && s.family != linux.AF_INET6 {
|
||||
// States not implemented for this socket's family.
|
||||
return 0
|
||||
@@ -3033,6 +3049,8 @@ func (s *SocketOperations) State() uint32 {
|
||||
}
|
||||
|
||||
// Type implements socket.Socket.Type.
|
||||
func (s *SocketOperations) Type() (family int, skType linux.SockType, protocol int) {
|
||||
func (s *socketOpsCommon) Type() (family int, skType linux.SockType, protocol int) {
|
||||
return s.family, s.skType, s.protocol
|
||||
}
|
||||
|
||||
// LINT.ThenChange(./netstack_vfs2.go)
|
||||
|
||||
@@ -0,0 +1,327 @@
|
||||
// Copyright 2018 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 netstack
|
||||
|
||||
import (
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/sockfs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/inet"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/sentry/socket/netfilter"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
"gvisor.dev/gvisor/pkg/syserr"
|
||||
"gvisor.dev/gvisor/pkg/syserror"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/usermem"
|
||||
"gvisor.dev/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
// SocketVFS2 encapsulates all the state needed to represent a network stack
|
||||
// endpoint in the kernel context.
|
||||
type SocketVFS2 struct {
|
||||
vfsfd vfs.FileDescription
|
||||
vfs.FileDescriptionDefaultImpl
|
||||
vfs.DentryMetadataFileDescriptionImpl
|
||||
|
||||
socketOpsCommon
|
||||
}
|
||||
|
||||
// NewVFS2 creates a new endpoint socket.
|
||||
func NewVFS2(t *kernel.Task, family int, skType linux.SockType, protocol int, queue *waiter.Queue, endpoint tcpip.Endpoint) (*vfs.FileDescription, *syserr.Error) {
|
||||
if skType == linux.SOCK_STREAM {
|
||||
if err := endpoint.SetSockOptBool(tcpip.DelayOption, true); err != nil {
|
||||
return nil, syserr.TranslateNetstackError(err)
|
||||
}
|
||||
}
|
||||
|
||||
mnt := t.Kernel().SocketMount()
|
||||
fs := mnt.Filesystem().Impl().(*kernfs.Filesystem)
|
||||
d := sockfs.NewDentry(t.Credentials(), fs.NextIno())
|
||||
|
||||
s := &SocketVFS2{
|
||||
socketOpsCommon: socketOpsCommon{
|
||||
Queue: queue,
|
||||
family: family,
|
||||
Endpoint: endpoint,
|
||||
skType: skType,
|
||||
protocol: protocol,
|
||||
},
|
||||
}
|
||||
vfsfd := &s.vfsfd
|
||||
if err := vfsfd.Init(s, linux.O_RDWR, mnt, d, &vfs.FileDescriptionOptions{
|
||||
DenyPRead: true,
|
||||
DenyPWrite: true,
|
||||
UseDentryMetadata: true,
|
||||
}); err != nil {
|
||||
return nil, syserr.FromError(err)
|
||||
}
|
||||
return vfsfd, nil
|
||||
}
|
||||
|
||||
// Readiness implements waiter.Waitable.Readiness.
|
||||
func (s *SocketVFS2) Readiness(mask waiter.EventMask) waiter.EventMask {
|
||||
return s.socketOpsCommon.Readiness(mask)
|
||||
}
|
||||
|
||||
// EventRegister implements waiter.Waitable.EventRegister.
|
||||
func (s *SocketVFS2) EventRegister(e *waiter.Entry, mask waiter.EventMask) {
|
||||
s.socketOpsCommon.EventRegister(e, mask)
|
||||
}
|
||||
|
||||
// EventUnregister implements waiter.Waitable.EventUnregister.
|
||||
func (s *SocketVFS2) EventUnregister(e *waiter.Entry) {
|
||||
s.socketOpsCommon.EventUnregister(e)
|
||||
}
|
||||
|
||||
// PRead implements vfs.FileDescriptionImpl.
|
||||
func (s *SocketVFS2) PRead(ctx context.Context, dst usermem.IOSequence, offset int64, opts vfs.ReadOptions) (int64, error) {
|
||||
return 0, syserror.ESPIPE
|
||||
}
|
||||
|
||||
// Read implements vfs.FileDescriptionImpl.
|
||||
func (s *SocketVFS2) Read(ctx context.Context, dst usermem.IOSequence, opts vfs.ReadOptions) (int64, error) {
|
||||
// All flags other than RWF_NOWAIT should be ignored.
|
||||
// TODO(gvisor.dev/issue/2601): Support RWF_NOWAIT.
|
||||
if opts.Flags != 0 {
|
||||
return 0, syserror.EOPNOTSUPP
|
||||
}
|
||||
|
||||
if dst.NumBytes() == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
n, _, _, _, _, err := s.nonBlockingRead(ctx, dst, false, false, false)
|
||||
if err == syserr.ErrWouldBlock {
|
||||
return int64(n), syserror.ErrWouldBlock
|
||||
}
|
||||
if err != nil {
|
||||
return 0, err.ToError()
|
||||
}
|
||||
return int64(n), nil
|
||||
}
|
||||
|
||||
// PWrite implements vfs.FileDescriptionImpl.
|
||||
func (s *SocketVFS2) PWrite(ctx context.Context, src usermem.IOSequence, offset int64, opts vfs.WriteOptions) (int64, error) {
|
||||
return 0, syserror.ESPIPE
|
||||
}
|
||||
|
||||
// Write implements vfs.FileDescriptionImpl.
|
||||
func (s *SocketVFS2) Write(ctx context.Context, src usermem.IOSequence, opts vfs.WriteOptions) (int64, error) {
|
||||
// All flags other than RWF_NOWAIT should be ignored.
|
||||
// TODO(gvisor.dev/issue/2601): Support RWF_NOWAIT.
|
||||
if opts.Flags != 0 {
|
||||
return 0, syserror.EOPNOTSUPP
|
||||
}
|
||||
|
||||
f := &ioSequencePayload{ctx: ctx, src: src}
|
||||
n, resCh, err := s.Endpoint.Write(f, tcpip.WriteOptions{})
|
||||
if err == tcpip.ErrWouldBlock {
|
||||
return 0, syserror.ErrWouldBlock
|
||||
}
|
||||
|
||||
if resCh != nil {
|
||||
t := kernel.TaskFromContext(ctx)
|
||||
if err := t.Block(resCh); err != nil {
|
||||
return 0, syserr.FromError(err).ToError()
|
||||
}
|
||||
|
||||
n, _, err = s.Endpoint.Write(f, tcpip.WriteOptions{})
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return 0, syserr.TranslateNetstackError(err).ToError()
|
||||
}
|
||||
|
||||
if int64(n) < src.NumBytes() {
|
||||
return int64(n), syserror.ErrWouldBlock
|
||||
}
|
||||
|
||||
return int64(n), nil
|
||||
}
|
||||
|
||||
// Accept implements the linux syscall accept(2) for sockets backed by
|
||||
// tcpip.Endpoint.
|
||||
func (s *SocketVFS2) Accept(t *kernel.Task, peerRequested bool, flags int, blocking bool) (int32, linux.SockAddr, uint32, *syserr.Error) {
|
||||
// Issue the accept request to get the new endpoint.
|
||||
ep, wq, terr := s.Endpoint.Accept()
|
||||
if terr != nil {
|
||||
if terr != tcpip.ErrWouldBlock || !blocking {
|
||||
return 0, nil, 0, syserr.TranslateNetstackError(terr)
|
||||
}
|
||||
|
||||
var err *syserr.Error
|
||||
ep, wq, err = s.blockingAccept(t)
|
||||
if err != nil {
|
||||
return 0, nil, 0, err
|
||||
}
|
||||
}
|
||||
|
||||
ns, err := NewVFS2(t, s.family, s.skType, s.protocol, wq, ep)
|
||||
if err != nil {
|
||||
return 0, nil, 0, err
|
||||
}
|
||||
defer ns.DecRef()
|
||||
|
||||
if err := ns.SetStatusFlags(t, t.Credentials(), uint32(flags&linux.SOCK_NONBLOCK)); err != nil {
|
||||
return 0, nil, 0, syserr.FromError(err)
|
||||
}
|
||||
|
||||
var addr linux.SockAddr
|
||||
var addrLen uint32
|
||||
if peerRequested {
|
||||
// Get address of the peer and write it to peer slice.
|
||||
var err *syserr.Error
|
||||
addr, addrLen, err = ns.Impl().(*SocketVFS2).GetPeerName(t)
|
||||
if err != nil {
|
||||
return 0, nil, 0, err
|
||||
}
|
||||
}
|
||||
|
||||
fd, e := t.NewFDFromVFS2(0, ns, kernel.FDFlags{
|
||||
CloseOnExec: flags&linux.SOCK_CLOEXEC != 0,
|
||||
})
|
||||
|
||||
t.Kernel().RecordSocketVFS2(ns)
|
||||
|
||||
return fd, addr, addrLen, syserr.FromError(e)
|
||||
}
|
||||
|
||||
// Ioctl implements vfs.FileDescriptionImpl.
|
||||
func (s *SocketVFS2) Ioctl(ctx context.Context, uio usermem.IO, args arch.SyscallArguments) (uintptr, error) {
|
||||
return s.socketOpsCommon.ioctl(ctx, uio, args)
|
||||
}
|
||||
|
||||
// GetSockOpt implements the linux syscall getsockopt(2) for sockets backed by
|
||||
// tcpip.Endpoint.
|
||||
func (s *SocketVFS2) GetSockOpt(t *kernel.Task, level, name int, outPtr usermem.Addr, outLen int) (interface{}, *syserr.Error) {
|
||||
// TODO(b/78348848): Unlike other socket options, SO_TIMESTAMP is
|
||||
// implemented specifically for netstack.SocketVFS2 rather than
|
||||
// commonEndpoint. commonEndpoint should be extended to support socket
|
||||
// options where the implementation is not shared, as unix sockets need
|
||||
// their own support for SO_TIMESTAMP.
|
||||
if level == linux.SOL_SOCKET && name == linux.SO_TIMESTAMP {
|
||||
if outLen < sizeOfInt32 {
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
val := int32(0)
|
||||
s.readMu.Lock()
|
||||
defer s.readMu.Unlock()
|
||||
if s.sockOptTimestamp {
|
||||
val = 1
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
if level == linux.SOL_TCP && name == linux.TCP_INQ {
|
||||
if outLen < sizeOfInt32 {
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
val := int32(0)
|
||||
s.readMu.Lock()
|
||||
defer s.readMu.Unlock()
|
||||
if s.sockOptInq {
|
||||
val = 1
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
|
||||
if s.skType == linux.SOCK_RAW && level == linux.IPPROTO_IP {
|
||||
switch name {
|
||||
case linux.IPT_SO_GET_INFO:
|
||||
if outLen < linux.SizeOfIPTGetinfo {
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
|
||||
stack := inet.StackFromContext(t)
|
||||
if stack == nil {
|
||||
return nil, syserr.ErrNoDevice
|
||||
}
|
||||
info, err := netfilter.GetInfo(t, stack.(*Stack).Stack, outPtr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return info, nil
|
||||
|
||||
case linux.IPT_SO_GET_ENTRIES:
|
||||
if outLen < linux.SizeOfIPTGetEntries {
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
|
||||
stack := inet.StackFromContext(t)
|
||||
if stack == nil {
|
||||
return nil, syserr.ErrNoDevice
|
||||
}
|
||||
entries, err := netfilter.GetEntries(t, stack.(*Stack).Stack, outPtr, outLen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return entries, nil
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return GetSockOpt(t, s, s.Endpoint, s.family, s.skType, level, name, outLen)
|
||||
}
|
||||
|
||||
// SetSockOpt implements the linux syscall setsockopt(2) for sockets backed by
|
||||
// tcpip.Endpoint.
|
||||
func (s *SocketVFS2) SetSockOpt(t *kernel.Task, level int, name int, optVal []byte) *syserr.Error {
|
||||
// TODO(b/78348848): Unlike other socket options, SO_TIMESTAMP is
|
||||
// implemented specifically for netstack.SocketVFS2 rather than
|
||||
// commonEndpoint. commonEndpoint should be extended to support socket
|
||||
// options where the implementation is not shared, as unix sockets need
|
||||
// their own support for SO_TIMESTAMP.
|
||||
if level == linux.SOL_SOCKET && name == linux.SO_TIMESTAMP {
|
||||
if len(optVal) < sizeOfInt32 {
|
||||
return syserr.ErrInvalidArgument
|
||||
}
|
||||
s.readMu.Lock()
|
||||
defer s.readMu.Unlock()
|
||||
s.sockOptTimestamp = usermem.ByteOrder.Uint32(optVal) != 0
|
||||
return nil
|
||||
}
|
||||
if level == linux.SOL_TCP && name == linux.TCP_INQ {
|
||||
if len(optVal) < sizeOfInt32 {
|
||||
return syserr.ErrInvalidArgument
|
||||
}
|
||||
s.readMu.Lock()
|
||||
defer s.readMu.Unlock()
|
||||
s.sockOptInq = usermem.ByteOrder.Uint32(optVal) != 0
|
||||
return nil
|
||||
}
|
||||
|
||||
if s.skType == linux.SOCK_RAW && level == linux.IPPROTO_IP {
|
||||
switch name {
|
||||
case linux.IPT_SO_SET_REPLACE:
|
||||
if len(optVal) < linux.SizeOfIPTReplace {
|
||||
return syserr.ErrInvalidArgument
|
||||
}
|
||||
|
||||
stack := inet.StackFromContext(t)
|
||||
if stack == nil {
|
||||
return syserr.ErrNoDevice
|
||||
}
|
||||
// Stack must be a netstack stack.
|
||||
return netfilter.SetEntries(stack.(*Stack).Stack, optVal)
|
||||
|
||||
case linux.IPT_SO_SET_ADD_COUNTERS:
|
||||
// TODO(gvisor.dev/issue/170): Counter support.
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return SetSockOpt(t, s, s.Endpoint, level, name, optVal)
|
||||
}
|
||||
@@ -33,6 +33,8 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
// LINT.IfChange
|
||||
|
||||
// provider is an inet socket provider.
|
||||
type provider struct {
|
||||
family int
|
||||
@@ -167,6 +169,8 @@ func packetSocket(t *kernel.Task, epStack *Stack, stype linux.SockType, protocol
|
||||
return New(t, linux.AF_PACKET, stype, protocol, wq, ep)
|
||||
}
|
||||
|
||||
// LINT.ThenChange(./provider_vfs2.go)
|
||||
|
||||
// Pair just returns nil sockets (not supported).
|
||||
func (*provider) Pair(*kernel.Task, linux.SockType, int) (*fs.File, *fs.File, *syserr.Error) {
|
||||
return nil, nil, nil
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
// 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 netstack
|
||||
|
||||
import (
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
"gvisor.dev/gvisor/pkg/sentry/socket"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
"gvisor.dev/gvisor/pkg/syserr"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
|
||||
"gvisor.dev/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
// providerVFS2 is an inet socket provider.
|
||||
type providerVFS2 struct {
|
||||
family int
|
||||
netProto tcpip.NetworkProtocolNumber
|
||||
}
|
||||
|
||||
// Socket creates a new socket object for the AF_INET, AF_INET6, or AF_PACKET
|
||||
// family.
|
||||
func (p *providerVFS2) Socket(t *kernel.Task, stype linux.SockType, protocol int) (*vfs.FileDescription, *syserr.Error) {
|
||||
// Fail right away if we don't have a stack.
|
||||
stack := t.NetworkContext()
|
||||
if stack == nil {
|
||||
// Don't propagate an error here. Instead, allow the socket
|
||||
// code to continue searching for another provider.
|
||||
return nil, nil
|
||||
}
|
||||
eps, ok := stack.(*Stack)
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Packet sockets are handled separately, since they are neither INET
|
||||
// nor INET6 specific.
|
||||
if p.family == linux.AF_PACKET {
|
||||
return packetSocketVFS2(t, eps, stype, protocol)
|
||||
}
|
||||
|
||||
// Figure out the transport protocol.
|
||||
transProto, associated, err := getTransportProtocol(t, stype, protocol)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create the endpoint.
|
||||
var ep tcpip.Endpoint
|
||||
var e *tcpip.Error
|
||||
wq := &waiter.Queue{}
|
||||
if stype == linux.SOCK_RAW {
|
||||
ep, e = eps.Stack.NewRawEndpoint(transProto, p.netProto, wq, associated)
|
||||
} else {
|
||||
ep, e = eps.Stack.NewEndpoint(transProto, p.netProto, wq)
|
||||
|
||||
// Assign task to PacketOwner interface to get the UID and GID for
|
||||
// iptables owner matching.
|
||||
if e == nil {
|
||||
ep.SetOwner(t)
|
||||
}
|
||||
}
|
||||
if e != nil {
|
||||
return nil, syserr.TranslateNetstackError(e)
|
||||
}
|
||||
|
||||
return NewVFS2(t, p.family, stype, int(transProto), wq, ep)
|
||||
}
|
||||
|
||||
func packetSocketVFS2(t *kernel.Task, epStack *Stack, stype linux.SockType, protocol int) (*vfs.FileDescription, *syserr.Error) {
|
||||
// Packet sockets require CAP_NET_RAW.
|
||||
creds := auth.CredentialsFromContext(t)
|
||||
if !creds.HasCapability(linux.CAP_NET_RAW) {
|
||||
return nil, syserr.ErrNotPermitted
|
||||
}
|
||||
|
||||
// "cooked" packets don't contain link layer information.
|
||||
var cooked bool
|
||||
switch stype {
|
||||
case linux.SOCK_DGRAM:
|
||||
cooked = true
|
||||
case linux.SOCK_RAW:
|
||||
cooked = false
|
||||
default:
|
||||
return nil, syserr.ErrProtocolNotSupported
|
||||
}
|
||||
|
||||
// protocol is passed in network byte order, but netstack wants it in
|
||||
// host order.
|
||||
netProto := tcpip.NetworkProtocolNumber(ntohs(uint16(protocol)))
|
||||
|
||||
wq := &waiter.Queue{}
|
||||
ep, err := epStack.Stack.NewPacketEndpoint(cooked, netProto, wq)
|
||||
if err != nil {
|
||||
return nil, syserr.TranslateNetstackError(err)
|
||||
}
|
||||
|
||||
return NewVFS2(t, linux.AF_PACKET, stype, protocol, wq, ep)
|
||||
}
|
||||
|
||||
// Pair just returns nil sockets (not supported).
|
||||
func (*providerVFS2) Pair(*kernel.Task, linux.SockType, int) (*vfs.FileDescription, *vfs.FileDescription, *syserr.Error) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
// init registers socket providers for AF_INET, AF_INET6, and AF_PACKET.
|
||||
func init() {
|
||||
// Providers backed by netstack.
|
||||
p := []providerVFS2{
|
||||
{
|
||||
family: linux.AF_INET,
|
||||
netProto: ipv4.ProtocolNumber,
|
||||
},
|
||||
{
|
||||
family: linux.AF_INET6,
|
||||
netProto: ipv6.ProtocolNumber,
|
||||
},
|
||||
{
|
||||
family: linux.AF_PACKET,
|
||||
},
|
||||
}
|
||||
|
||||
for i := range p {
|
||||
socket.RegisterProviderVFS2(p[i].family, &p[i])
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user