mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Merge branch 'master' into ip-forwarding
- Merges aleksej-paschenko's with HEAD - Adds vfs2 support for ip_forward
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
load("//tools/go_stateify:defs.bzl", "go_library")
|
||||
load("//tools:defs.bzl", "go_library")
|
||||
|
||||
package(licenses = ["notice"])
|
||||
|
||||
@@ -7,44 +7,52 @@ go_library(
|
||||
srcs = [
|
||||
"device.go",
|
||||
"netstack.go",
|
||||
"netstack_vfs2.go",
|
||||
"provider.go",
|
||||
"provider_vfs2.go",
|
||||
"save_restore.go",
|
||||
"stack.go",
|
||||
],
|
||||
importpath = "gvisor.dev/gvisor/pkg/sentry/socket/netstack",
|
||||
visibility = [
|
||||
"//pkg/sentry:internal",
|
||||
],
|
||||
deps = [
|
||||
"//pkg/abi/linux",
|
||||
"//pkg/amutex",
|
||||
"//pkg/binary",
|
||||
"//pkg/context",
|
||||
"//pkg/log",
|
||||
"//pkg/metric",
|
||||
"//pkg/safemem",
|
||||
"//pkg/sentry/arch",
|
||||
"//pkg/sentry/context",
|
||||
"//pkg/sentry/device",
|
||||
"//pkg/sentry/fs",
|
||||
"//pkg/sentry/fs/fsutil",
|
||||
"//pkg/sentry/fs/lock",
|
||||
"//pkg/sentry/fsimpl/sockfs",
|
||||
"//pkg/sentry/inet",
|
||||
"//pkg/sentry/kernel",
|
||||
"//pkg/sentry/kernel/auth",
|
||||
"//pkg/sentry/kernel/time",
|
||||
"//pkg/sentry/safemem",
|
||||
"//pkg/sentry/socket",
|
||||
"//pkg/sentry/socket/netfilter",
|
||||
"//pkg/sentry/unimpl",
|
||||
"//pkg/sentry/usermem",
|
||||
"//pkg/sentry/vfs",
|
||||
"//pkg/sync",
|
||||
"//pkg/syserr",
|
||||
"//pkg/syserror",
|
||||
"//pkg/tcpip",
|
||||
"//pkg/tcpip/buffer",
|
||||
"//pkg/tcpip/header",
|
||||
"//pkg/tcpip/iptables",
|
||||
"//pkg/tcpip/network/ipv4",
|
||||
"//pkg/tcpip/network/ipv6",
|
||||
"//pkg/tcpip/stack",
|
||||
"//pkg/tcpip/transport/tcp",
|
||||
"//pkg/tcpip/transport/udp",
|
||||
"//pkg/usermem",
|
||||
"//pkg/waiter",
|
||||
"//tools/go_marshal/marshal",
|
||||
"//tools/go_marshal/primitive",
|
||||
"@org_golang_x_sys//unix:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,332 @@
|
||||
// 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/amutex"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
fslock "gvisor.dev/gvisor/pkg/sentry/fs/lock"
|
||||
"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"
|
||||
"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"
|
||||
"gvisor.dev/gvisor/tools/go_marshal/marshal"
|
||||
"gvisor.dev/gvisor/tools/go_marshal/primitive"
|
||||
)
|
||||
|
||||
// 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
|
||||
vfs.LockFD
|
||||
|
||||
socketOpsCommon
|
||||
}
|
||||
|
||||
var _ = socket.SocketVFS2(&SocketVFS2{})
|
||||
|
||||
// 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()
|
||||
d := sockfs.NewDentry(t.Credentials(), mnt)
|
||||
|
||||
s := &SocketVFS2{
|
||||
socketOpsCommon: socketOpsCommon{
|
||||
Queue: queue,
|
||||
family: family,
|
||||
Endpoint: endpoint,
|
||||
skType: skType,
|
||||
protocol: protocol,
|
||||
},
|
||||
}
|
||||
s.LockFD.Init(&vfs.FileLocks{})
|
||||
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)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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 {
|
||||
if err := amutex.Block(ctx, resCh); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
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(t)
|
||||
|
||||
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) (marshal.Marshallable, *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 := primitive.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 := primitive.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)
|
||||
}
|
||||
|
||||
// LockPOSIX implements vfs.FileDescriptionImpl.LockPOSIX.
|
||||
func (s *SocketVFS2) LockPOSIX(ctx context.Context, uid fslock.UniqueID, t fslock.LockType, start, length uint64, whence int16, block fslock.Blocker) error {
|
||||
return s.Locks().LockPOSIX(ctx, &s.vfsfd, uid, t, start, length, whence, block)
|
||||
}
|
||||
|
||||
// UnlockPOSIX implements vfs.FileDescriptionImpl.UnlockPOSIX.
|
||||
func (s *SocketVFS2) UnlockPOSIX(ctx context.Context, uid fslock.UniqueID, start, length uint64, whence int16) error {
|
||||
return s.Locks().UnlockPOSIX(ctx, &s.vfsfd, uid, start, length, whence)
|
||||
}
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
"syscall"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/sentry/context"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
@@ -33,6 +33,8 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
// LINT.IfChange
|
||||
|
||||
// provider is an inet socket provider.
|
||||
type provider struct {
|
||||
family int
|
||||
@@ -62,10 +64,6 @@ func getTransportProtocol(ctx context.Context, stype linux.SockType, protocol in
|
||||
}
|
||||
|
||||
case linux.SOCK_RAW:
|
||||
// TODO(b/142504697): "In order to create a raw socket, a
|
||||
// process must have the CAP_NET_RAW capability in the user
|
||||
// namespace that governs its network namespace." - raw(7)
|
||||
|
||||
// Raw sockets require CAP_NET_RAW.
|
||||
creds := auth.CredentialsFromContext(ctx)
|
||||
if !creds.HasCapability(linux.CAP_NET_RAW) {
|
||||
@@ -75,6 +73,8 @@ func getTransportProtocol(ctx context.Context, stype linux.SockType, protocol in
|
||||
switch protocol {
|
||||
case syscall.IPPROTO_ICMP:
|
||||
return header.ICMPv4ProtocolNumber, true, nil
|
||||
case syscall.IPPROTO_ICMPV6:
|
||||
return header.ICMPv6ProtocolNumber, true, nil
|
||||
case syscall.IPPROTO_UDP:
|
||||
return header.UDPProtocolNumber, true, nil
|
||||
case syscall.IPPROTO_TCP:
|
||||
@@ -124,6 +124,12 @@ func (p *provider) Socket(t *kernel.Task, stype linux.SockType, protocol int) (*
|
||||
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)
|
||||
@@ -133,10 +139,6 @@ func (p *provider) Socket(t *kernel.Task, stype linux.SockType, protocol int) (*
|
||||
}
|
||||
|
||||
func packetSocket(t *kernel.Task, epStack *Stack, stype linux.SockType, protocol int) (*fs.File, *syserr.Error) {
|
||||
// TODO(b/142504697): "In order to create a packet socket, a process
|
||||
// must have the CAP_NET_RAW capability in the user namespace that
|
||||
// governs its network namespace." - packet(7)
|
||||
|
||||
// Packet sockets require CAP_NET_RAW.
|
||||
creds := auth.CredentialsFromContext(t)
|
||||
if !creds.HasCapability(linux.CAP_NET_RAW) {
|
||||
@@ -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])
|
||||
}
|
||||
}
|
||||
@@ -15,14 +15,15 @@
|
||||
package netstack
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/sentry/inet"
|
||||
"gvisor.dev/gvisor/pkg/sentry/socket/netfilter"
|
||||
"gvisor.dev/gvisor/pkg/syserr"
|
||||
"gvisor.dev/gvisor/pkg/syserror"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/iptables"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
@@ -41,19 +42,29 @@ func (s *Stack) SupportsIPv6() bool {
|
||||
return s.Stack.CheckNetworkProtocol(ipv6.ProtocolNumber)
|
||||
}
|
||||
|
||||
// Converts Netstack's ARPHardwareType to equivalent linux constants.
|
||||
func toLinuxARPHardwareType(t header.ARPHardwareType) uint16 {
|
||||
switch t {
|
||||
case header.ARPHardwareNone:
|
||||
return linux.ARPHRD_NONE
|
||||
case header.ARPHardwareLoopback:
|
||||
return linux.ARPHRD_LOOPBACK
|
||||
case header.ARPHardwareEther:
|
||||
return linux.ARPHRD_ETHER
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown ARPHRD type: %d", t))
|
||||
}
|
||||
}
|
||||
|
||||
// Interfaces implements inet.Stack.Interfaces.
|
||||
func (s *Stack) Interfaces() map[int32]inet.Interface {
|
||||
is := make(map[int32]inet.Interface)
|
||||
for id, ni := range s.Stack.NICInfo() {
|
||||
var devType uint16
|
||||
if ni.Flags.Loopback {
|
||||
devType = linux.ARPHRD_LOOPBACK
|
||||
}
|
||||
is[int32(id)] = inet.Interface{
|
||||
Name: ni.Name,
|
||||
Addr: []byte(ni.LinkAddress),
|
||||
Flags: uint32(nicStateFlagsToLinux(ni.Flags)),
|
||||
DeviceType: devType,
|
||||
DeviceType: toLinuxARPHardwareType(ni.ARPHardwareType),
|
||||
MTU: ni.MTU,
|
||||
}
|
||||
}
|
||||
@@ -89,6 +100,59 @@ func (s *Stack) InterfaceAddrs() map[int32][]inet.InterfaceAddr {
|
||||
return nicAddrs
|
||||
}
|
||||
|
||||
// AddInterfaceAddr implements inet.Stack.AddInterfaceAddr.
|
||||
func (s *Stack) AddInterfaceAddr(idx int32, addr inet.InterfaceAddr) error {
|
||||
var (
|
||||
protocol tcpip.NetworkProtocolNumber
|
||||
address tcpip.Address
|
||||
)
|
||||
switch addr.Family {
|
||||
case linux.AF_INET:
|
||||
if len(addr.Addr) < header.IPv4AddressSize {
|
||||
return syserror.EINVAL
|
||||
}
|
||||
if addr.PrefixLen > header.IPv4AddressSize*8 {
|
||||
return syserror.EINVAL
|
||||
}
|
||||
protocol = ipv4.ProtocolNumber
|
||||
address = tcpip.Address(addr.Addr[:header.IPv4AddressSize])
|
||||
|
||||
case linux.AF_INET6:
|
||||
if len(addr.Addr) < header.IPv6AddressSize {
|
||||
return syserror.EINVAL
|
||||
}
|
||||
if addr.PrefixLen > header.IPv6AddressSize*8 {
|
||||
return syserror.EINVAL
|
||||
}
|
||||
protocol = ipv6.ProtocolNumber
|
||||
address = tcpip.Address(addr.Addr[:header.IPv6AddressSize])
|
||||
|
||||
default:
|
||||
return syserror.ENOTSUP
|
||||
}
|
||||
|
||||
protocolAddress := tcpip.ProtocolAddress{
|
||||
Protocol: protocol,
|
||||
AddressWithPrefix: tcpip.AddressWithPrefix{
|
||||
Address: address,
|
||||
PrefixLen: int(addr.PrefixLen),
|
||||
},
|
||||
}
|
||||
|
||||
// Attach address to interface.
|
||||
if err := s.Stack.AddProtocolAddressWithOptions(tcpip.NICID(idx), protocolAddress, stack.CanBePrimaryEndpoint); err != nil {
|
||||
return syserr.TranslateNetstackError(err).ToError()
|
||||
}
|
||||
|
||||
// Add route for local network.
|
||||
s.Stack.AddRoute(tcpip.Route{
|
||||
Destination: protocolAddress.AddressWithPrefix.Subnet(),
|
||||
Gateway: "", // No gateway for local network.
|
||||
NIC: tcpip.NICID(idx),
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// TCPReceiveBufferSize implements inet.Stack.TCPReceiveBufferSize.
|
||||
func (s *Stack) TCPReceiveBufferSize() (inet.TCPBufferSize, error) {
|
||||
var rs tcp.ReceiveBufferSizeOption
|
||||
@@ -143,39 +207,83 @@ func (s *Stack) SetTCPSACKEnabled(enabled bool) error {
|
||||
return syserr.TranslateNetstackError(s.Stack.SetTransportProtocolOption(tcp.ProtocolNumber, tcp.SACKEnabled(enabled))).ToError()
|
||||
}
|
||||
|
||||
// TCPRecovery implements inet.Stack.TCPRecovery.
|
||||
func (s *Stack) TCPRecovery() (inet.TCPLossRecovery, error) {
|
||||
var recovery tcp.Recovery
|
||||
if err := s.Stack.TransportProtocolOption(tcp.ProtocolNumber, &recovery); err != nil {
|
||||
return 0, syserr.TranslateNetstackError(err).ToError()
|
||||
}
|
||||
return inet.TCPLossRecovery(recovery), nil
|
||||
}
|
||||
|
||||
// SetTCPRecovery implements inet.Stack.SetTCPRecovery.
|
||||
func (s *Stack) SetTCPRecovery(recovery inet.TCPLossRecovery) error {
|
||||
return syserr.TranslateNetstackError(s.Stack.SetTransportProtocolOption(tcp.ProtocolNumber, tcp.Recovery(recovery))).ToError()
|
||||
}
|
||||
|
||||
// Statistics implements inet.Stack.Statistics.
|
||||
func (s *Stack) Statistics(stat interface{}, arg string) error {
|
||||
switch stats := stat.(type) {
|
||||
case *inet.StatDev:
|
||||
for _, ni := range s.Stack.NICInfo() {
|
||||
if ni.Name != arg {
|
||||
continue
|
||||
}
|
||||
// TODO(gvisor.dev/issue/2103) Support stubbed stats.
|
||||
*stats = inet.StatDev{
|
||||
// Receive section.
|
||||
ni.Stats.Rx.Bytes.Value(), // bytes.
|
||||
ni.Stats.Rx.Packets.Value(), // packets.
|
||||
0, // errs.
|
||||
0, // drop.
|
||||
0, // fifo.
|
||||
0, // frame.
|
||||
0, // compressed.
|
||||
0, // multicast.
|
||||
// Transmit section.
|
||||
ni.Stats.Tx.Bytes.Value(), // bytes.
|
||||
ni.Stats.Tx.Packets.Value(), // packets.
|
||||
0, // errs.
|
||||
0, // drop.
|
||||
0, // fifo.
|
||||
0, // colls.
|
||||
0, // carrier.
|
||||
0, // compressed.
|
||||
}
|
||||
break
|
||||
}
|
||||
case *inet.StatSNMPIP:
|
||||
ip := Metrics.IP
|
||||
// TODO(gvisor.dev/issue/969) Support stubbed stats.
|
||||
*stats = inet.StatSNMPIP{
|
||||
0, // TODO(gvisor.dev/issue/969): Support Ip/Forwarding.
|
||||
0, // TODO(gvisor.dev/issue/969): Support Ip/DefaultTTL.
|
||||
ip.PacketsReceived.Value(), // InReceives.
|
||||
0, // TODO(gvisor.dev/issue/969): Support Ip/InHdrErrors.
|
||||
ip.InvalidAddressesReceived.Value(), // InAddrErrors.
|
||||
0, // TODO(gvisor.dev/issue/969): Support Ip/ForwDatagrams.
|
||||
0, // TODO(gvisor.dev/issue/969): Support Ip/InUnknownProtos.
|
||||
0, // TODO(gvisor.dev/issue/969): Support Ip/InDiscards.
|
||||
ip.PacketsDelivered.Value(), // InDelivers.
|
||||
ip.PacketsSent.Value(), // OutRequests.
|
||||
ip.OutgoingPacketErrors.Value(), // OutDiscards.
|
||||
0, // TODO(gvisor.dev/issue/969): Support Ip/OutNoRoutes.
|
||||
0, // TODO(gvisor.dev/issue/969): Support Ip/ReasmTimeout.
|
||||
0, // TODO(gvisor.dev/issue/969): Support Ip/ReasmReqds.
|
||||
0, // TODO(gvisor.dev/issue/969): Support Ip/ReasmOKs.
|
||||
0, // TODO(gvisor.dev/issue/969): Support Ip/ReasmFails.
|
||||
0, // TODO(gvisor.dev/issue/969): Support Ip/FragOKs.
|
||||
0, // TODO(gvisor.dev/issue/969): Support Ip/FragFails.
|
||||
0, // TODO(gvisor.dev/issue/969): Support Ip/FragCreates.
|
||||
0, // Ip/Forwarding.
|
||||
0, // Ip/DefaultTTL.
|
||||
ip.PacketsReceived.Value(), // InReceives.
|
||||
0, // Ip/InHdrErrors.
|
||||
ip.InvalidDestinationAddressesReceived.Value(), // InAddrErrors.
|
||||
0, // Ip/ForwDatagrams.
|
||||
0, // Ip/InUnknownProtos.
|
||||
0, // Ip/InDiscards.
|
||||
ip.PacketsDelivered.Value(), // InDelivers.
|
||||
ip.PacketsSent.Value(), // OutRequests.
|
||||
ip.OutgoingPacketErrors.Value(), // OutDiscards.
|
||||
0, // Ip/OutNoRoutes.
|
||||
0, // Support Ip/ReasmTimeout.
|
||||
0, // Support Ip/ReasmReqds.
|
||||
0, // Support Ip/ReasmOKs.
|
||||
0, // Support Ip/ReasmFails.
|
||||
0, // Support Ip/FragOKs.
|
||||
0, // Support Ip/FragFails.
|
||||
0, // Support Ip/FragCreates.
|
||||
}
|
||||
case *inet.StatSNMPICMP:
|
||||
in := Metrics.ICMP.V4PacketsReceived.ICMPv4PacketStats
|
||||
out := Metrics.ICMP.V4PacketsSent.ICMPv4PacketStats
|
||||
// TODO(gvisor.dev/issue/969) Support stubbed stats.
|
||||
*stats = inet.StatSNMPICMP{
|
||||
0, // TODO(gvisor.dev/issue/969): Support Icmp/InMsgs.
|
||||
0, // Icmp/InMsgs.
|
||||
Metrics.ICMP.V4PacketsSent.Dropped.Value(), // InErrors.
|
||||
0, // TODO(gvisor.dev/issue/969): Support Icmp/InCsumErrors.
|
||||
0, // Icmp/InCsumErrors.
|
||||
in.DstUnreachable.Value(), // InDestUnreachs.
|
||||
in.TimeExceeded.Value(), // InTimeExcds.
|
||||
in.ParamProblem.Value(), // InParmProbs.
|
||||
@@ -187,7 +295,7 @@ func (s *Stack) Statistics(stat interface{}, arg string) error {
|
||||
in.TimestampReply.Value(), // InTimestampReps.
|
||||
in.InfoRequest.Value(), // InAddrMasks.
|
||||
in.InfoReply.Value(), // InAddrMaskReps.
|
||||
0, // TODO(gvisor.dev/issue/969): Support Icmp/OutMsgs.
|
||||
0, // Icmp/OutMsgs.
|
||||
Metrics.ICMP.V4PacketsReceived.Invalid.Value(), // OutErrors.
|
||||
out.DstUnreachable.Value(), // OutDestUnreachs.
|
||||
out.TimeExceeded.Value(), // OutTimeExcds.
|
||||
@@ -223,15 +331,16 @@ func (s *Stack) Statistics(stat interface{}, arg string) error {
|
||||
}
|
||||
case *inet.StatSNMPUDP:
|
||||
udp := Metrics.UDP
|
||||
// TODO(gvisor.dev/issue/969) Support stubbed stats.
|
||||
*stats = inet.StatSNMPUDP{
|
||||
udp.PacketsReceived.Value(), // InDatagrams.
|
||||
udp.UnknownPortErrors.Value(), // NoPorts.
|
||||
0, // TODO(gvisor.dev/issue/969): Support Udp/InErrors.
|
||||
0, // Udp/InErrors.
|
||||
udp.PacketsSent.Value(), // OutDatagrams.
|
||||
udp.ReceiveBufferErrors.Value(), // RcvbufErrors.
|
||||
0, // TODO(gvisor.dev/issue/969): Support Udp/SndbufErrors.
|
||||
0, // TODO(gvisor.dev/issue/969): Support Udp/InCsumErrors.
|
||||
0, // TODO(gvisor.dev/issue/969): Support Udp/IgnoredMulti.
|
||||
0, // Udp/SndbufErrors.
|
||||
udp.ChecksumErrors.Value(), // Udp/InCsumErrors.
|
||||
0, // Udp/IgnoredMulti.
|
||||
}
|
||||
default:
|
||||
return syserr.ErrEndpointOperation.ToError()
|
||||
@@ -278,21 +387,30 @@ func (s *Stack) RouteTable() []inet.Route {
|
||||
}
|
||||
|
||||
// IPTables returns the stack's iptables.
|
||||
func (s *Stack) IPTables() (iptables.IPTables, error) {
|
||||
func (s *Stack) IPTables() (*stack.IPTables, error) {
|
||||
return s.Stack.IPTables(), nil
|
||||
}
|
||||
|
||||
// FillDefaultIPTables sets the stack's iptables to the default tables, which
|
||||
// allow and do not modify all traffic.
|
||||
func (s *Stack) FillDefaultIPTables() {
|
||||
netfilter.FillDefaultIPTables(s.Stack)
|
||||
}
|
||||
|
||||
// Resume implements inet.Stack.Resume.
|
||||
func (s *Stack) Resume() {
|
||||
s.Stack.Resume()
|
||||
}
|
||||
|
||||
// RegisteredEndpoints implements inet.Stack.RegisteredEndpoints.
|
||||
func (s *Stack) RegisteredEndpoints() []stack.TransportEndpoint {
|
||||
return s.Stack.RegisteredEndpoints()
|
||||
}
|
||||
|
||||
// CleanupEndpoints implements inet.Stack.CleanupEndpoints.
|
||||
func (s *Stack) CleanupEndpoints() []stack.TransportEndpoint {
|
||||
return s.Stack.CleanupEndpoints()
|
||||
}
|
||||
|
||||
// RestoreCleanupEndpoints implements inet.Stack.RestoreCleanupEndpoints.
|
||||
func (s *Stack) RestoreCleanupEndpoints(es []stack.TransportEndpoint) {
|
||||
s.Stack.RestoreCleanupEndpoints(es)
|
||||
}
|
||||
|
||||
// Forwarding implements inet.Stack.Forwarding.
|
||||
func (s *Stack) Forwarding(protocol tcpip.NetworkProtocolNumber) bool {
|
||||
switch protocol {
|
||||
|
||||
Reference in New Issue
Block a user