mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Support plugin network stack
This commit supports a third-party network stack as a plugin stack for gVisor. The overall plugin package structure is the following: - pkg/sentry/socket/plugin: Interfaces for initializing plugin network stack. It will be used in network setting up during sandbox creating. - pkg/sentry/socket/plugin/stack: Glue layer for plugin stack's socket and stack ops with sentry. It will also register plugin stack operations if imported. - pkg/sentry/socket/plugin/cgo: Interfaces defined in C for plugin network stack to support. To build target runsc-plugin-stack, which imports pkg/sentry/socket/plugin/stack package and enables CGO: bazel build --config=plugin-tldk runsc:runsc-plugin-stack (i.e. --config=plugin-tldk indicates that using TLDK as plugin stack) By using runsc-plugin-stack binary and setting "--network=plugin" in runtimeArgs, user can use third-party network stack instead of netstack embedded in gVisor to get better network performance. Redis benchmark with following setups: 1. KVM platform 2. 4 physical cores for target pod 3. target pod as redis server Runc: $redis-benchmark -h [target ip] -n 100000 -t get,set -q SET: 115207.38 requests per second, p50=0.215 msec GET: 92336.11 requests per second, p50=0.279 msec $redis-benchmark -h [target ip] -n 100000 -t get,set -q SET: 113895.21 requests per second, p50=0.247 msec GET: 96899.23 requests per second, p50=0.271 msec $redis-benchmark -h [target ip] -n 100000 -t get,set -q SET: 126582.27 requests per second, p50=0.199 msec GET: 95969.28 requests per second, p50=0.271 msec Runsc with plugin stack: $redis-benchmark -h [target ip] -n 100000 -t get,set -q SET: 123915.74 requests per second, p50=0.343 msec GET: 115473.45 requests per second, p50=0.335 msec $redis-benchmark -h [target ip] -n 100000 -t get,set -q SET: 120918.98 requests per second, p50=0.351 msec GET: 117647.05 requests per second, p50=0.351 msec $redis-benchmark -h [target ip] -n 100000 -t get,set -q SET: 119904.08 requests per second, p50=0.367 msec GET: 112739.57 requests per second, p50=0.375 msec Runsc with netstack: $redis-benchmark -h [target ip] -n 100000 -t get,set -q SET: 59952.04 requests per second, p50=0.759 msec GET: 61162.08 requests per second, p50=0.631 msec $redis-benchmark -h [target ip] -n 100000 -t get,set -q SET: 52219.32 requests per second, p50=0.719 msec GET: 58719.91 requests per second, p50=0.663 msec $redis-benchmark -h [target ip] -n 100000 -t get,set -q SET: 59952.04 requests per second, p50=0.751 msec GET: 60827.25 requests per second, p50=0.751 msec Updates https://github.com/google/gvisor/issues/9266 Co-developed-by: Tianyu Zhou <wentong.zty@antgroup.com> Signed-off-by: Anqi Shen <amy.saq@antgroup.com>
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
package(licenses = ["notice"])
|
||||
|
||||
load("//tools:defs.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "stack",
|
||||
srcs = [
|
||||
"notifier.go",
|
||||
"provider.go",
|
||||
"readwriter.go",
|
||||
"socket.go",
|
||||
"stack.go",
|
||||
"util.go",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/abi/linux",
|
||||
"//pkg/abi/linux/errno",
|
||||
"//pkg/binary",
|
||||
"//pkg/context",
|
||||
"//pkg/errors/linuxerr",
|
||||
"//pkg/hostarch",
|
||||
"//pkg/marshal",
|
||||
"//pkg/marshal/primitive",
|
||||
"//pkg/safemem",
|
||||
"//pkg/sentry/arch",
|
||||
"//pkg/sentry/fsimpl/sockfs",
|
||||
"//pkg/sentry/inet",
|
||||
"//pkg/sentry/kernel",
|
||||
"//pkg/sentry/kernel/auth",
|
||||
"//pkg/sentry/kernel/time",
|
||||
"//pkg/sentry/socket",
|
||||
"//pkg/sentry/socket/plugin",
|
||||
"//pkg/sentry/socket/plugin/cgo",
|
||||
"//pkg/sentry/unimpl",
|
||||
"//pkg/sentry/vfs",
|
||||
"//pkg/syserr",
|
||||
"//pkg/tcpip",
|
||||
"//pkg/tcpip/network/ipv4",
|
||||
"//pkg/tcpip/network/ipv6",
|
||||
"//pkg/usermem",
|
||||
"//pkg/waiter",
|
||||
"@org_golang_x_sys//unix:go_default_library",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,165 @@
|
||||
// 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 (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/sentry/socket/plugin"
|
||||
"gvisor.dev/gvisor/pkg/sentry/socket/plugin/cgo"
|
||||
"gvisor.dev/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
// Notifier holds all the state necessary to issue notifications when
|
||||
// IO events occur on the observed FDs in plugin stack.
|
||||
type Notifier struct {
|
||||
// the epoll FD used to register for io notifications.
|
||||
epFD int32
|
||||
|
||||
// mu protects eventMap.
|
||||
mu sync.Mutex
|
||||
|
||||
// eventMap maps file descriptors to their notification queues
|
||||
// and waiting status.
|
||||
eventMap map[uint32]*plugin.EventInfo
|
||||
}
|
||||
|
||||
const (
|
||||
MaxEpollEvents = 128
|
||||
SleepInMsecond = 100
|
||||
)
|
||||
|
||||
// NewNotifier initialize the event notifier for plugin stack.
|
||||
// It will allocate a eventMap with fd as key and corresponding eventInfo
|
||||
// as value and start a goroutine waiting the arrival of events.
|
||||
func NewNotifier() *Notifier {
|
||||
ioInit := make(chan int32)
|
||||
|
||||
n := &Notifier{
|
||||
eventMap: make(map[uint32]*plugin.EventInfo),
|
||||
}
|
||||
|
||||
go n.waitAndNotify(ioInit)
|
||||
|
||||
epFD := <-ioInit
|
||||
if epFD < 0 {
|
||||
return nil
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// AddFD implements plugin.PluginNotifier.AddFD.
|
||||
func (n *Notifier) AddFD(fd uint32, eventInfo *plugin.EventInfo) {
|
||||
n.mu.Lock()
|
||||
defer n.mu.Unlock()
|
||||
|
||||
// Panic if we're already notifying on this FD.
|
||||
if _, ok := n.eventMap[fd]; ok {
|
||||
panic(fmt.Sprintf("File descriptor %d added twice", fd))
|
||||
}
|
||||
|
||||
// We have nothing to wait for at the moment. Just add it to the map.
|
||||
n.eventMap[fd] = eventInfo
|
||||
}
|
||||
|
||||
// RemoveFD implements plugin.PluginNotifier.RemoveFD.
|
||||
func (n *Notifier) RemoveFD(fd uint32) {
|
||||
n.mu.Lock()
|
||||
defer n.mu.Unlock()
|
||||
delete(n.eventMap, fd)
|
||||
}
|
||||
|
||||
// UpdateFD implements plugin.PluginNotifier.UpdateFD.
|
||||
func (n *Notifier) UpdateFD(fd uint32) {
|
||||
n.mu.Lock()
|
||||
defer n.mu.Unlock()
|
||||
|
||||
if eventInfo, ok := n.eventMap[fd]; ok {
|
||||
n.waitFD(fd, eventInfo)
|
||||
}
|
||||
}
|
||||
|
||||
// waitAndNotify loops waiting for io event notifications from the epoll
|
||||
// object. Once notifications arrive, they are dispatched to the
|
||||
// registered queue.
|
||||
func (n *Notifier) waitAndNotify(ioInit chan int32) error {
|
||||
// plugin stack leverages TLS varaibles, so bind this goroutine with
|
||||
// one specific OS thread
|
||||
runtime.LockOSThread()
|
||||
|
||||
// If current thread is not the main thread, change the thread name.
|
||||
if syscall.Getpid() != syscall.Gettid() {
|
||||
threadName := []byte("io-thread\x00")
|
||||
if err := unix.Prctl(unix.PR_SET_NAME, uintptr(cgo.GetPtr(threadName)), 0, 0, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
n.epFD = int32(cgo.EpollCreate())
|
||||
|
||||
ioInit <- n.epFD
|
||||
|
||||
var events [MaxEpollEvents]syscall.EpollEvent
|
||||
for {
|
||||
num := cgo.EpollWait(n.epFD, events[:], MaxEpollEvents, SleepInMsecond)
|
||||
if num <= 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
n.mu.Lock()
|
||||
for i := 0; i < num; i++ {
|
||||
h := uint32(events[i].Fd)
|
||||
eventInfo, ok := n.eventMap[h]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
ev := waiter.EventMask(events[i].Events)
|
||||
eventInfo.Ready |= ev & (eventInfo.Mask | waiter.EventErr | waiter.EventHUp)
|
||||
// When an error occurred, invoke all events
|
||||
if ev&(waiter.EventErr|waiter.EventHUp) != 0 {
|
||||
ev |= waiter.EventIn | waiter.EventOut
|
||||
}
|
||||
eventInfo.Wq.Notify(ev)
|
||||
}
|
||||
n.mu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func (n *Notifier) waitFD(fd uint32, eventInfo *plugin.EventInfo) {
|
||||
mask := eventInfo.Wq.Events()
|
||||
|
||||
eventInfo.Mask = mask
|
||||
if !eventInfo.Waiting && mask == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
switch {
|
||||
case !eventInfo.Waiting && mask != 0:
|
||||
cgo.EpollCtl(n.epFD, syscall.EPOLL_CTL_ADD, fd, uint32(mask))
|
||||
eventInfo.Waiting = true
|
||||
case eventInfo.Waiting && mask == 0:
|
||||
cgo.EpollCtl(n.epFD, syscall.EPOLL_CTL_DEL, fd, uint32(mask))
|
||||
eventInfo.Ready = 0
|
||||
eventInfo.Waiting = false
|
||||
case eventInfo.Waiting && mask != 0:
|
||||
cgo.EpollCtl(n.epFD, syscall.EPOLL_CTL_MOD, fd, uint32(mask))
|
||||
eventInfo.Ready &= mask
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
// 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/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
"gvisor.dev/gvisor/pkg/sentry/socket"
|
||||
"gvisor.dev/gvisor/pkg/sentry/socket/plugin/cgo"
|
||||
"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"
|
||||
)
|
||||
|
||||
type provider struct {
|
||||
family int
|
||||
netProto tcpip.NetworkProtocolNumber
|
||||
}
|
||||
|
||||
// Socket creates a new socket object for the AF_INET or AF_INET6 family.
|
||||
func (p *provider) Socket(t *kernel.Task, skType linux.SockType, protocol int) (*vfs.FileDescription, *syserr.Error) {
|
||||
// Fail right away if there is no plugin stack registered.
|
||||
ctx := t.NetworkContext()
|
||||
if ctx == nil {
|
||||
return nil, nil
|
||||
}
|
||||
_, ok := ctx.(*Stack)
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Only accept TCP and UDP.
|
||||
stype := skType & linux.SOCK_TYPE_MASK
|
||||
switch stype {
|
||||
case syscall.SOCK_STREAM:
|
||||
switch protocol {
|
||||
case 0, syscall.IPPROTO_TCP:
|
||||
default:
|
||||
return nil, syserr.ErrProtocolNotSupported
|
||||
}
|
||||
case syscall.SOCK_DGRAM:
|
||||
switch protocol {
|
||||
case 0, syscall.IPPROTO_UDP:
|
||||
default:
|
||||
return nil, syserr.ErrProtocolNotSupported
|
||||
}
|
||||
case syscall.SOCK_RAW:
|
||||
// Raw sockets require CAP_NET_RAW.
|
||||
creds := auth.CredentialsFromContext(t)
|
||||
if !creds.HasCapability(linux.CAP_NET_RAW) {
|
||||
return nil, syserr.ErrPermissionDenied
|
||||
}
|
||||
default:
|
||||
return nil, syserr.ErrSocketNotSupported
|
||||
}
|
||||
|
||||
handle := cgo.Socket(p.family, int(skType), protocol)
|
||||
if handle < 0 {
|
||||
return nil, int2err(handle)
|
||||
}
|
||||
|
||||
fd, err := newSocket(t, p.family, skType, protocol, stack.notifier, int(handle), uint32(skType&syscall.SOCK_NONBLOCK))
|
||||
return fd, err
|
||||
}
|
||||
|
||||
// Pair just returns nil sockets (not supported).
|
||||
func (*provider) Pair(*kernel.Task, linux.SockType, int) (*vfs.FileDescription, *vfs.FileDescription, *syserr.Error) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Providers backed by plugin stack.
|
||||
p := []provider{
|
||||
{
|
||||
family: linux.AF_INET,
|
||||
netProto: ipv4.ProtocolNumber,
|
||||
},
|
||||
|
||||
{
|
||||
family: linux.AF_INET6,
|
||||
netProto: ipv6.ProtocolNumber,
|
||||
},
|
||||
}
|
||||
|
||||
for i := range p {
|
||||
socket.RegisterProvider(p[i].family, &p[i])
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
// 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 (
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/safemem"
|
||||
"gvisor.dev/gvisor/pkg/sentry/socket/plugin/cgo"
|
||||
)
|
||||
|
||||
type pluginStackRW struct {
|
||||
handle uint32
|
||||
|
||||
// Represent both input and output flags.
|
||||
flags uint32
|
||||
|
||||
// Reused as msg_control for read.
|
||||
to []byte
|
||||
|
||||
iovs [3]syscall.Iovec
|
||||
}
|
||||
|
||||
var pluginStackRWPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &pluginStackRW{}
|
||||
},
|
||||
}
|
||||
|
||||
func getReadWriter(handle uint32) *pluginStackRW {
|
||||
rw := pluginStackRWPool.Get().(*pluginStackRW)
|
||||
rw.handle = handle
|
||||
return rw
|
||||
}
|
||||
|
||||
func putReadWriter(rw *pluginStackRW) {
|
||||
*rw = pluginStackRW{}
|
||||
pluginStackRWPool.Put(rw)
|
||||
}
|
||||
|
||||
// ReadToBlocks implements safemem.Reader.ReadToBlocks.
|
||||
func (rw *pluginStackRW) ReadToBlocks(dsts safemem.BlockSeq) (uint64, error) {
|
||||
// Set MSG_DONTWAIT flag to avoid blocking in plugin stack.
|
||||
flags := int(rw.flags) & ^linux.MSG_DONTWAIT
|
||||
if len(rw.to) != 0 || flags != 0 {
|
||||
iovs := iovecsFromBlockSeq(dsts, rw)
|
||||
rc, _, lc, mflags := cgo.Recvmsg(rw.handle, iovs, nil, rw.to, int(rw.flags))
|
||||
if rc >= 0 {
|
||||
rw.to = rw.to[:lc]
|
||||
rw.flags = uint32(mflags)
|
||||
}
|
||||
return translateReturn(rc)
|
||||
}
|
||||
|
||||
var rc int64
|
||||
if dsts.IsEmpty() {
|
||||
rc = 0
|
||||
} else if dsts.NumBlocks() == 1 {
|
||||
rc = cgo.Read(rw.handle, dsts.Head().Addr(), dsts.Head().Len())
|
||||
} else {
|
||||
rc = cgo.Readv(rw.handle, iovecsFromBlockSeq(dsts, rw))
|
||||
}
|
||||
|
||||
return translateReturn(rc)
|
||||
}
|
||||
|
||||
// WriteFromBlocks implements safemem.Writer.WriteFromBlocks.
|
||||
//
|
||||
// Preconditions: rw.d.metadataMu must be locked.
|
||||
func (rw *pluginStackRW) WriteFromBlocks(srcs safemem.BlockSeq) (uint64, error) {
|
||||
var rc int64
|
||||
|
||||
if rw.to != nil {
|
||||
if srcs.IsEmpty() {
|
||||
// Invoke plugin stack checking whether there is any error to report
|
||||
// on target socket which sends 0-length data.
|
||||
rc = cgo.Sendto(rw.handle, 0, 0, 0, rw.to)
|
||||
} else if srcs.NumBlocks() == 1 {
|
||||
rc = cgo.Sendto(rw.handle, srcs.Head().Addr(), srcs.Head().Len(), 0, rw.to)
|
||||
} else {
|
||||
iovs := iovecsFromBlockSeq(srcs, rw)
|
||||
rc = cgo.Sendmsg(rw.handle, iovs, rw.to, 0)
|
||||
}
|
||||
} else {
|
||||
if srcs.IsEmpty() {
|
||||
// Invoke plugin stack checking whether there is any error to report
|
||||
// on target socket which sends 0-length data.
|
||||
rc = cgo.Write(rw.handle, 0, 0)
|
||||
} else if srcs.NumBlocks() == 1 {
|
||||
rc = cgo.Write(rw.handle, srcs.Head().Addr(), srcs.Head().Len())
|
||||
} else {
|
||||
rc = cgo.Writev(rw.handle, iovecsFromBlockSeq(srcs, rw))
|
||||
}
|
||||
}
|
||||
return translateReturn(rc)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,86 @@
|
||||
// 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 provides an implementation of plugin.PluginStack
|
||||
// interface and an implementation of socket.Socket interface.
|
||||
//
|
||||
// It glues sentry interfaces with plugin netstack interfaces defined in cgo.
|
||||
package stack
|
||||
|
||||
import (
|
||||
"gvisor.dev/gvisor/pkg/errors/linuxerr"
|
||||
"gvisor.dev/gvisor/pkg/sentry/inet"
|
||||
"gvisor.dev/gvisor/pkg/sentry/socket/plugin"
|
||||
"gvisor.dev/gvisor/pkg/sentry/socket/plugin/cgo"
|
||||
)
|
||||
|
||||
// Stack is a struct that interacts with third-party network stack.
|
||||
// It implements inet.Stack and plugin.PluginStack.
|
||||
type Stack struct {
|
||||
inet.Stack
|
||||
|
||||
notifier *Notifier
|
||||
}
|
||||
|
||||
var stack *Stack
|
||||
|
||||
// Init implements plugin.PluginStack.Init.
|
||||
func (s *Stack) Init(args *plugin.InitStackArgs) error {
|
||||
if err := cgo.InitStack(args.InitStr, args.FDs); err != nil {
|
||||
return err
|
||||
}
|
||||
s.notifier = NewNotifier()
|
||||
stack = s
|
||||
return nil
|
||||
}
|
||||
|
||||
// PreInit implements plugin.PluginStack.PreInit.
|
||||
func (s *Stack) PreInit(args *plugin.PreInitStackArgs) (string, []int, error) {
|
||||
return cgo.PreInitStack(args.Pid)
|
||||
}
|
||||
|
||||
// Interfaces implements inet.Stack.Interfaces.
|
||||
func (s *Stack) Interfaces() map[int32]inet.Interface {
|
||||
// TODO: support Interfaces
|
||||
return make(map[int32]inet.Interface)
|
||||
}
|
||||
|
||||
// InterfaceAddrs implements inet.Stack.InterfaceAddrs.
|
||||
func (s *Stack) InterfaceAddrs() map[int32][]inet.InterfaceAddr {
|
||||
// TODO: support InterfaceAddrs
|
||||
return make(map[int32][]inet.InterfaceAddr)
|
||||
}
|
||||
|
||||
// AddInterfaceAddr implements inet.Stack.AddInterfaceAddr.
|
||||
func (s *Stack) AddInterfaceAddr(idx int32, addr inet.InterfaceAddr) error {
|
||||
return linuxerr.EACCES
|
||||
}
|
||||
|
||||
// RemoveInterfaceAddr implements inet.Stack.RemoveInterfaceAddr.
|
||||
func (s *Stack) RemoveInterfaceAddr(int32, inet.InterfaceAddr) error {
|
||||
return linuxerr.EACCES
|
||||
}
|
||||
|
||||
// SupportsIPv6 implements Stack.SupportsIPv6.
|
||||
func (s *Stack) SupportsIPv6() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Destroy implements inet.Stack.Destroy.
|
||||
func (*Stack) Destroy() {
|
||||
}
|
||||
|
||||
func init() {
|
||||
plugin.RegisterPluginStack(&Stack{})
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
// 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 (
|
||||
"net"
|
||||
"syscall"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/abi/linux/errno"
|
||||
"gvisor.dev/gvisor/pkg/hostarch"
|
||||
"gvisor.dev/gvisor/pkg/safemem"
|
||||
"gvisor.dev/gvisor/pkg/sentry/inet"
|
||||
"gvisor.dev/gvisor/pkg/sentry/socket"
|
||||
"gvisor.dev/gvisor/pkg/sentry/socket/plugin/cgo"
|
||||
"gvisor.dev/gvisor/pkg/syserr"
|
||||
)
|
||||
|
||||
func int2err(from int64) *syserr.Error {
|
||||
if from >= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if (-from) == errno.EAGAIN {
|
||||
return syserr.ErrWouldBlock
|
||||
}
|
||||
|
||||
return syserr.FromHost(syscall.Errno(-from))
|
||||
}
|
||||
|
||||
func translateReturn(ret int64) (uint64, error) {
|
||||
if ret < 0 {
|
||||
return 0, int2err(ret).ToError()
|
||||
} else if ret == 0 {
|
||||
return 0, nil
|
||||
} else {
|
||||
return uint64(ret), nil
|
||||
}
|
||||
}
|
||||
|
||||
func copyAddrOut(ifr *linux.IFReq, ifaceAddr *inet.InterfaceAddr) {
|
||||
hostarch.ByteOrder.PutUint16(ifr.Data[0:2], uint16(ifaceAddr.Family))
|
||||
hostarch.ByteOrder.PutUint16(ifr.Data[2:4], 0) // port
|
||||
if ifaceAddr.Family == linux.AF_INET {
|
||||
copy(ifr.Data[4:8], net.IP(ifaceAddr.Addr).To4()[:4])
|
||||
} else {
|
||||
copy(ifr.Data[8:24], ifaceAddr.Addr[:16])
|
||||
}
|
||||
}
|
||||
|
||||
func iovecsFromBlockSeq(bs safemem.BlockSeq, rw *pluginStackRW) []syscall.Iovec {
|
||||
var iovs []syscall.Iovec
|
||||
if rw != nil {
|
||||
// Reuse the old buffer and set length to zero.
|
||||
iovs = rw.iovs[:0]
|
||||
}
|
||||
for ; !bs.IsEmpty(); bs = bs.Tail() {
|
||||
b := bs.Head()
|
||||
iovs = append(iovs, syscall.Iovec{
|
||||
Base: &b.ToSlice()[0],
|
||||
Len: uint64(b.Len()),
|
||||
})
|
||||
}
|
||||
return iovs
|
||||
}
|
||||
|
||||
func buildControlMessage(controlData []byte) *socket.ControlMessages {
|
||||
controlMessages := socket.ControlMessages{}
|
||||
if len(controlData) >= 28 {
|
||||
timebytes := controlData[12:]
|
||||
timeval := (*linux.Timeval)(cgo.GetPtr(timebytes))
|
||||
m := socket.IPControlMessages{
|
||||
HasTimestamp: true,
|
||||
Timestamp: timeval.ToTime(),
|
||||
}
|
||||
controlMessages.IP = m
|
||||
}
|
||||
return &controlMessages
|
||||
}
|
||||
Reference in New Issue
Block a user