mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Merge pull request #9551 from amysaq2023:support-external-stack
PiperOrigin-RevId: 677933413
This commit is contained in:
@@ -29,6 +29,9 @@ test:race --@io_bazel_rules_go//go/config:race --@io_bazel_rules_go//go/config:p
|
||||
build --@io_bazel_rules_go//go/config:pure
|
||||
test --@io_bazel_rules_go//go/config:pure
|
||||
|
||||
# Set bazel_rule as non-pure when cgo is used.
|
||||
build:plugin-tldk --@io_bazel_rules_go//go/config:pure=false --define=plugin_tldk=true --define=network_plugins=true
|
||||
|
||||
# By default, exclude nogo targets from building. They will still be included
|
||||
# by default for all tests.
|
||||
build --build_tag_filters=-nogo
|
||||
|
||||
@@ -87,7 +87,7 @@ steps:
|
||||
label: ":world_map: Build runsc and pkg (AMD64)"
|
||||
commands:
|
||||
- "make build TARGETS=//pkg/..."
|
||||
- "make build TARGETS=//runsc/..."
|
||||
- "make build TARGETS='--build_tag_filters=-network_plugins //runsc/...'"
|
||||
agents:
|
||||
arch: "amd64"
|
||||
|
||||
@@ -96,7 +96,7 @@ steps:
|
||||
label: ":world_map: Build runsc and pkg (ARM64)"
|
||||
commands:
|
||||
- "make build TARGETS=//pkg/..."
|
||||
- "make build TARGETS=//runsc/..."
|
||||
- "make build TARGETS='--build_tag_filters=-network_plugins //runsc/...'"
|
||||
agents:
|
||||
arch: "arm64"
|
||||
|
||||
@@ -105,7 +105,7 @@ steps:
|
||||
<<: *source_test_continuous
|
||||
label: ":world_map: Build everything"
|
||||
commands:
|
||||
- "make build TARGETS=//..."
|
||||
- "make build TARGETS='--build_tag_filters=-network_plugins //...'"
|
||||
|
||||
# Check that the Go branch builds. This is not technically required, as this build is maintained
|
||||
# as a GitHub action in order to preserve this maintaince across forks. However, providing the
|
||||
|
||||
@@ -139,6 +139,7 @@ go_path(
|
||||
|
||||
# Packages that are not dependencies of the above.
|
||||
"//pkg/sentry/kernel/memevent",
|
||||
"//pkg/sentry/socket/plugin/stack",
|
||||
"//pkg/tcpip/adapters/gonet",
|
||||
"//pkg/tcpip/faketime",
|
||||
"//pkg/tcpip/link/channel",
|
||||
|
||||
@@ -221,7 +221,7 @@ nogo-tests:
|
||||
#
|
||||
# FIXME(gvisor.dev/issue/10045): Need to fix broken tests.
|
||||
unit-tests: ## Local package unit tests in pkg/..., tools/.., etc.
|
||||
@$(call test,--test_tag_filters=-nogo$(COMMA)-requires-kvm -- //:all pkg/... tools/... runsc/... vdso/... test/trace/... -//pkg/metric:metric_test -//pkg/coretag:coretag_test -//runsc/config:config_test -//tools/tracereplay:tracereplay_test -//test/trace:trace_test)
|
||||
@$(call test,--test_tag_filters=-nogo$(COMMA)-requires-kvm --build_tag_filters=-network_plugins -- //:all pkg/... tools/... runsc/... vdso/... test/trace/... -//pkg/metric:metric_test -//pkg/coretag:coretag_test -//runsc/config:config_test -//tools/tracereplay:tracereplay_test -//test/trace:trace_test)
|
||||
.PHONY: unit-tests
|
||||
|
||||
# See unit-tests: this includes runsc/container.
|
||||
|
||||
@@ -52,6 +52,7 @@ http_archive(
|
||||
# Allow for patching of the go_sdk.
|
||||
"//tools:rules_go_sdk.patch",
|
||||
"//tools:rules_go_facts.patch",
|
||||
"//tools:rules_cgo.patch",
|
||||
],
|
||||
sha256 = "80a98277ad1311dacd837f9b16db62887702e9f1d1c4c9f796d0121a46c8e184",
|
||||
urls = [
|
||||
@@ -3351,3 +3352,9 @@ go_repository(
|
||||
sum = "h1:uImZAk6qLkC6F9ju6mZ5SPBqTyK8xjZKwSmwnCg4bxg=",
|
||||
version = "v2.3.3",
|
||||
)
|
||||
|
||||
new_local_repository(
|
||||
name = "libpluginstack",
|
||||
path = "tools/plugin-stack",
|
||||
build_file = "tools/plugin-stack/plugin-stack.BUILD",
|
||||
)
|
||||
|
||||
@@ -85,6 +85,9 @@ type IFConf struct {
|
||||
Ptr uint64
|
||||
}
|
||||
|
||||
// SizeOfIFConf is the binary size of an IFConf struct (16 bytes).
|
||||
var SizeOfIFConf = (*IFConf)(nil).SizeBytes()
|
||||
|
||||
// EthtoolCmd is a marshallable type to be able to easily copyin the
|
||||
// the command for an SIOCETHTOOL ioctl.
|
||||
//
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
load("//tools:defs.bzl", "go_library")
|
||||
|
||||
package(
|
||||
default_applicable_licenses = ["//:license"],
|
||||
licenses = ["notice"],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "plugin",
|
||||
srcs = [
|
||||
"config.go",
|
||||
"plugin.go",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/seccomp",
|
||||
"//pkg/sentry/inet",
|
||||
"//pkg/waiter",
|
||||
"@org_golang_x_sys//unix:go_default_library",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,41 @@
|
||||
load("//tools:defs.bzl", "go_library")
|
||||
|
||||
package(
|
||||
default_applicable_licenses = ["//:license"],
|
||||
licenses = ["notice"],
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "network_plugins",
|
||||
values = {"define": "network_plugins=true"},
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "cgo",
|
||||
srcs = [
|
||||
"cgo.go",
|
||||
"nocgo_stub_unsafe.go",
|
||||
"socket_unsafe.go",
|
||||
"stack_unsafe.go",
|
||||
"util_unsafe.go",
|
||||
],
|
||||
bazel_cdeps = [
|
||||
"@libpluginstack//:libpluginstack",
|
||||
],
|
||||
bazel_cgo = select({
|
||||
":network_plugins": True,
|
||||
"//conditions:default": False,
|
||||
}),
|
||||
bazel_clinkopts = [
|
||||
"-L external/libpluginstack",
|
||||
],
|
||||
bazel_copts = [
|
||||
"-march=native",
|
||||
"-I external/libpluginstack/lib/libtle_glue",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/abi/linux",
|
||||
"//pkg/abi/linux/errno",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright 2024 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 cgo provides interfaces definition to interact with third-party
|
||||
// network stack. It also implements CGO wrappers to handle Golang arguments
|
||||
// to CGO and CGO return values to Golang.
|
||||
//
|
||||
// Third-party external network stack will implement interfaces defined in this
|
||||
// package in order to be used by gVisor.
|
||||
package cgo
|
||||
@@ -0,0 +1,158 @@
|
||||
// Copyright 2024 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.
|
||||
|
||||
//go:build !network_plugins
|
||||
// +build !network_plugins
|
||||
|
||||
package cgo
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// GetPtr is a non-cgo stub function.
|
||||
func GetPtr(bs []byte) unsafe.Pointer {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// EpollCreate is a non-cgo stub function.
|
||||
func EpollCreate() int {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// EpollCtl is a non-cgo stub function.
|
||||
func EpollCtl(epfd int32, op int, handle, events uint32) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// EpollWait is a non-cgo stub function.
|
||||
func EpollWait(epfd int32, events []syscall.EpollEvent, n int, us int) int {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// Socket is a non-cgo stub function.
|
||||
func Socket(domain, skType, protocol int) int64 {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// Bind is a non-cgo stub function.
|
||||
func Bind(handle uint32, sa []byte) int64 {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// Listen is a non-cgo stub function.
|
||||
func Listen(handle uint32, backlog int) int64 {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// Accept is a non-cgo stub function.
|
||||
func Accept(handle uint32, addrPtr *byte, lenPtr *uint32) int64 {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// Ioctl is a non-cgo stub function.
|
||||
func Ioctl(handle uint32, cmd uint32, buf []byte) int64 {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// Connect is a non-cgo stub function.
|
||||
func Connect(handle uint32, addr []byte) int64 {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// Getsockopt is a non-cgo stub function.
|
||||
func Getsockopt(handle uint32, l int, n int, val []byte, s int) (int64, int) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// Setsockopt is a non-cgo stub function.
|
||||
func Setsockopt(handle uint32, l int, n int, val []byte) int64 {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// Shutdown is a non-cgo stub function.
|
||||
func Shutdown(handle uint32, how int) int64 {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// Close is a non-cgo stub function.
|
||||
func Close(handle uint32) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// Getsockname is a non-cgo stub function.
|
||||
func Getsockname(handle uint32, addr []byte, addrlen *uint32) int64 {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// GetPeername is a non-cgo stub function.
|
||||
func GetPeername(handle uint32, addr []byte, addrlen *uint32) int64 {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// Readiness is a non-cgo stub function.
|
||||
func Readiness(handle uint32, mask uint64) int64 {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// Read is a non-cgo stub function.
|
||||
func Read(handle uint32, buf uintptr, count int) int64 {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// Readv is a non-cgo stub function.
|
||||
func Readv(handle uint32, iovs []syscall.Iovec) int64 {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// Recvfrom is a non-cgo stub function.
|
||||
func Recvfrom(handle uint32, buf, addr []byte, flags int) (int64, int) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// Recvmsg is a non-cgo stub function.
|
||||
func Recvmsg(handle uint32, iovs []syscall.Iovec, addr, control []byte, flags int) (int64, int, int, int) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// Write is a non-cgo stub function.
|
||||
func Write(handle uint32, buf uintptr, count int) int64 {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// Writev is a non-cgo stub function.
|
||||
func Writev(handle uint32, iovs []syscall.Iovec) int64 {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// Sendto is a non-cgo stub function.
|
||||
func Sendto(handle uint32, buf uintptr, count int, flags int, addr []byte) int64 {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// Sendmsg is a non-cgo stub function.
|
||||
func Sendmsg(handle uint32, iovs []syscall.Iovec, addr []byte, flags int) int64 {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// InitStack is a non-cgo stub function.
|
||||
func InitStack(initStr string, fds []int) error {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// PreInitStack is a non-cgo stub function.
|
||||
func PreInitStack(pid int) (string, []int, error) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
@@ -0,0 +1,422 @@
|
||||
// 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.
|
||||
|
||||
//go:build network_plugins
|
||||
// +build network_plugins
|
||||
|
||||
package cgo
|
||||
|
||||
/*
|
||||
#include <stdint.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
// socket event-related operations
|
||||
int plugin_epoll_create(void);
|
||||
int plugin_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
|
||||
int plugin_epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
|
||||
|
||||
// socket control-path operations
|
||||
int plugin_socket(int domain, int type, int protocol, uint64_t *err);
|
||||
int plugin_listen(int sockfd, int backlog, uint64_t *err);
|
||||
int plugin_bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen, uint64_t *err);
|
||||
int plugin_accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen, uint64_t *err);
|
||||
int plugin_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen, uint64_t *err);
|
||||
int plugin_getsockopt(int sockfd, int level, int optname,
|
||||
void *optval, socklen_t *optlen, uint64_t *err);
|
||||
int plugin_setsockopt(int sockfd, int level, int optname,
|
||||
const void *optval, socklen_t optlen, uint64_t *err);
|
||||
int plugin_getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen, uint64_t *err);
|
||||
int plugin_getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen, uint64_t *err);
|
||||
int plugin_ioctl(int fd, uint64_t *err, unsigned long int request, void *buf);
|
||||
int plugin_shutdown(int sockfd, int how, uint64_t *err);
|
||||
int plugin_close(int fd);
|
||||
int plugin_readiness(int fd, int events);
|
||||
|
||||
// socket data-path (ingress) operations
|
||||
ssize_t plugin_recv(int sockfd, void *buf, size_t len, int flags, uint64_t *err);
|
||||
ssize_t plugin_recvfrom(int sockfd, void *buf, size_t len, int flags,
|
||||
struct sockaddr *src_addr, socklen_t *addrlen, uint64_t *err);
|
||||
ssize_t plugin_recvmsg(int sockfd, struct msghdr *msg, int flags, uint64_t *err);
|
||||
ssize_t plugin_read(int fd, void *buf, size_t count, uint64_t *err);
|
||||
ssize_t plugin_readv(int fd, const struct iovec *iov, int iovcnt, uint64_t *err);
|
||||
|
||||
// socket data-path (egress) operations
|
||||
ssize_t plugin_send(int sockfd, const void *buf, size_t len, int flags, uint64_t *err);
|
||||
ssize_t plugin_sendto(int sockfd, const void *buf, size_t len, int flags,
|
||||
const struct sockaddr *dest_addr, socklen_t addrlen, uint64_t *err);
|
||||
ssize_t plugin_sendmsg(int sockfd, const struct msghdr *msg, int flags, uint64_t *err);
|
||||
ssize_t plugin_write(int fd, const void *buf, size_t count, uint64_t *err);
|
||||
ssize_t plugin_writev(int fd, const struct iovec *iov, int iovcnt, uint64_t *err);
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
linuxerrno "gvisor.dev/gvisor/pkg/abi/linux/errno"
|
||||
)
|
||||
|
||||
// EpollCreate works as a CGO wrapper for plugin_epoll_create.
|
||||
func EpollCreate() int {
|
||||
return int(C.plugin_epoll_create())
|
||||
}
|
||||
|
||||
// EpollCtl works as a CGO wrapper for plugin_epoll_ctl.
|
||||
func EpollCtl(epfd int32, op int, handle, events uint32) {
|
||||
epollEvent := syscall.EpollEvent{
|
||||
Events: events,
|
||||
Fd: int32(handle),
|
||||
}
|
||||
C.plugin_epoll_ctl(
|
||||
C.int(epfd),
|
||||
C.int(op),
|
||||
C.int(handle),
|
||||
(*C.struct_epoll_event)(unsafe.Pointer(&epollEvent)))
|
||||
}
|
||||
|
||||
// EpollWait works as a CGO wrapper for plugin_epoll_wait.
|
||||
func EpollWait(epfd int32, events []syscall.EpollEvent, n int, us int) int {
|
||||
if len(events) == 0 {
|
||||
return 0
|
||||
}
|
||||
return int(C.plugin_epoll_wait(
|
||||
C.int(epfd),
|
||||
(*C.struct_epoll_event)(unsafe.Pointer(&events[0])),
|
||||
C.int(n),
|
||||
C.int(us)))
|
||||
}
|
||||
|
||||
// Socket works as a CGO wrapper for plugin_socket.
|
||||
// Note: This function will set socket as non-blocking.
|
||||
func Socket(domain, skType, protocol int) int64 {
|
||||
var errno uint64
|
||||
if fd := int64(C.plugin_socket(
|
||||
C.int(domain),
|
||||
C.int(skType),
|
||||
C.int(protocol),
|
||||
(*C.uint64_t)(unsafe.Pointer(&errno)))); fd < 0 {
|
||||
return -int64(errno)
|
||||
} else {
|
||||
nonblock := 1
|
||||
C.plugin_ioctl(
|
||||
C.int(fd),
|
||||
(*C.uint64_t)(unsafe.Pointer(&errno)),
|
||||
C.uint64_t(linux.FIONBIO),
|
||||
unsafe.Pointer(&nonblock))
|
||||
return fd
|
||||
}
|
||||
}
|
||||
|
||||
// Bind works as a CGO wrapper for plugin_bind.
|
||||
func Bind(handle uint32, sa []byte) int64 {
|
||||
var errno uint64
|
||||
return convertRetVal(
|
||||
int64(C.plugin_bind(
|
||||
C.int(handle),
|
||||
(*C.struct_sockaddr)(GetPtr(sa)),
|
||||
C.uint(len(sa)),
|
||||
(*C.uint64_t)(unsafe.Pointer(&errno)))),
|
||||
errno)
|
||||
}
|
||||
|
||||
// Listen works as a CGO wrapper for plugin_listen.
|
||||
func Listen(handle uint32, backlog int) int64 {
|
||||
var errno uint64
|
||||
return convertRetVal(
|
||||
int64(C.plugin_listen(
|
||||
C.int(handle),
|
||||
C.int(backlog),
|
||||
(*C.uint64_t)(unsafe.Pointer(&errno)))),
|
||||
errno)
|
||||
}
|
||||
|
||||
// Accept works as a CGO wrapper for plugin_accept.
|
||||
// Note: This function will set socket as non-blocking.
|
||||
func Accept(handle uint32, addrPtr *byte, lenPtr *uint32) int64 {
|
||||
var errno uint64
|
||||
if fd := int64(C.plugin_accept(
|
||||
C.int(handle),
|
||||
(*C.struct_sockaddr)(unsafe.Pointer(addrPtr)),
|
||||
(*C.socklen_t)(unsafe.Pointer(lenPtr)),
|
||||
(*C.uint64_t)(unsafe.Pointer(&errno)))); fd < 0 {
|
||||
return -int64(errno)
|
||||
} else {
|
||||
nonblock := 1
|
||||
C.plugin_ioctl(
|
||||
C.int(fd),
|
||||
(*C.uint64_t)(unsafe.Pointer(&errno)),
|
||||
C.uint64_t(linux.FIONBIO),
|
||||
unsafe.Pointer(&nonblock))
|
||||
return fd
|
||||
}
|
||||
}
|
||||
|
||||
// Ioctl works as a CGO wrapper for plugin_ioctl.
|
||||
func Ioctl(handle uint32, cmd uint32, buf []byte) int64 {
|
||||
var errno uint64
|
||||
return convertRetVal(
|
||||
int64(C.plugin_ioctl(
|
||||
C.int(handle),
|
||||
(*C.uint64_t)(unsafe.Pointer(&errno)),
|
||||
C.uint64_t(cmd),
|
||||
GetPtr(buf))),
|
||||
errno)
|
||||
}
|
||||
|
||||
// Connect works as a CGO wrapper for plugin_connect.
|
||||
func Connect(handle uint32, addr []byte) int64 {
|
||||
var errno uint64
|
||||
return convertRetVal(
|
||||
int64(C.plugin_connect(
|
||||
C.int(handle),
|
||||
(*C.struct_sockaddr)(GetPtr(addr)),
|
||||
C.socklen_t(len(addr)),
|
||||
(*C.uint64_t)(unsafe.Pointer(&errno)))),
|
||||
errno)
|
||||
}
|
||||
|
||||
// Getsockopt works as a CGO wrapper for plugin_getsockopt.
|
||||
func Getsockopt(handle uint32, l int, n int, val []byte, s int) (int64, int) {
|
||||
var errno uint64
|
||||
if ret := int64(C.plugin_getsockopt(
|
||||
C.int(handle),
|
||||
C.int(l),
|
||||
C.int(n),
|
||||
GetPtr(val),
|
||||
(*C.uint)(unsafe.Pointer(&s)),
|
||||
(*C.uint64_t)(unsafe.Pointer(&errno)))); ret < 0 {
|
||||
return -int64(errno), s
|
||||
} else {
|
||||
return ret, s
|
||||
}
|
||||
}
|
||||
|
||||
// Setsockopt works as a CGO wrapper for plugin_setsockopt.
|
||||
func Setsockopt(handle uint32, l int, n int, val []byte) int64 {
|
||||
var errno uint64
|
||||
return convertRetVal(
|
||||
int64(C.plugin_setsockopt(
|
||||
C.int(handle),
|
||||
C.int(l),
|
||||
C.int(n),
|
||||
GetPtr(val),
|
||||
C.uint(len(val)),
|
||||
(*C.uint64_t)(unsafe.Pointer(&errno)))),
|
||||
errno)
|
||||
}
|
||||
|
||||
// Shutdown works as a CGO wrapper for plugin_shutdown.
|
||||
func Shutdown(handle uint32, how int) int64 {
|
||||
var errno uint64
|
||||
return convertRetVal(
|
||||
int64(C.plugin_shutdown(
|
||||
C.int(handle),
|
||||
C.int(how),
|
||||
(*C.uint64_t)(unsafe.Pointer(&errno)))),
|
||||
errno)
|
||||
}
|
||||
|
||||
// Close works as a CGO wrapper for plugin_close.
|
||||
func Close(handle uint32) {
|
||||
C.plugin_close(C.int(handle))
|
||||
}
|
||||
|
||||
// Getsockname works as a CGO wrapper for plugin_getsockname.
|
||||
func Getsockname(handle uint32, addr []byte, addrlen *uint32) int64 {
|
||||
var errno uint64
|
||||
if len(addr) == 0 {
|
||||
return -linuxerrno.EINVAL
|
||||
}
|
||||
return convertRetVal(
|
||||
int64(C.plugin_getsockname(
|
||||
C.int(handle),
|
||||
(*C.struct_sockaddr)(unsafe.Pointer(&addr[0])),
|
||||
(*C.socklen_t)(unsafe.Pointer(addrlen)),
|
||||
(*C.uint64_t)(unsafe.Pointer(&errno)))),
|
||||
errno)
|
||||
}
|
||||
|
||||
// GetPeername works as a CGO wrapper for plugin_getpeername.
|
||||
func GetPeername(handle uint32, addr []byte, addrlen *uint32) int64 {
|
||||
var errno uint64
|
||||
if len(addr) == 0 {
|
||||
return -linuxerrno.EINVAL
|
||||
}
|
||||
return convertRetVal(
|
||||
int64(C.plugin_getpeername(
|
||||
C.int(handle),
|
||||
(*C.struct_sockaddr)(unsafe.Pointer(&addr[0])),
|
||||
(*C.socklen_t)(unsafe.Pointer(addrlen)),
|
||||
(*C.uint64_t)(unsafe.Pointer(&errno)))),
|
||||
errno)
|
||||
}
|
||||
|
||||
// Readiness works as a CGO wrapper for plugin_readiness.
|
||||
func Readiness(handle uint32, mask uint64) int64 {
|
||||
return int64(C.plugin_readiness(C.int(handle), C.int(mask)))
|
||||
}
|
||||
|
||||
// Read works as a CGO wrapper for plugin_read.
|
||||
func Read(handle uint32, buf uintptr, count int) int64 {
|
||||
var errno uint64
|
||||
return convertRetVal(
|
||||
int64(C.plugin_read(
|
||||
C.int(handle),
|
||||
unsafe.Pointer(buf),
|
||||
C.size_t(count),
|
||||
(*C.uint64_t)(unsafe.Pointer(&errno)))),
|
||||
errno)
|
||||
}
|
||||
|
||||
// Readv works as a CGO wrapper for plugin_readv.
|
||||
func Readv(handle uint32, iovs []syscall.Iovec) int64 {
|
||||
var errno uint64
|
||||
if len(iovs) == 0 {
|
||||
return 0
|
||||
}
|
||||
return convertRetVal(
|
||||
int64(C.plugin_readv(
|
||||
C.int(handle),
|
||||
(*C.struct_iovec)(unsafe.Pointer(&iovs[0])),
|
||||
C.int(len(iovs)),
|
||||
(*C.uint64_t)(unsafe.Pointer(&errno)))),
|
||||
errno)
|
||||
}
|
||||
|
||||
// Recvfrom works as a CGO wrapper for plugin_recvfrom.
|
||||
func Recvfrom(handle uint32, buf, addr []byte, flags int) (int64, int) {
|
||||
var errno uint64
|
||||
addrlen := len(addr)
|
||||
if ret := int64(C.plugin_recvfrom(
|
||||
C.int(handle),
|
||||
GetPtr(buf),
|
||||
C.size_t(len(buf)),
|
||||
C.int(flags),
|
||||
(*C.struct_sockaddr)(GetPtr(addr)),
|
||||
(*C.socklen_t)(unsafe.Pointer(&addrlen)),
|
||||
(*C.uint64_t)(unsafe.Pointer(&errno)))); ret < 0 {
|
||||
return -int64(errno), addrlen
|
||||
} else {
|
||||
return ret, addrlen
|
||||
}
|
||||
}
|
||||
|
||||
// Recvmsg works as a CGO wrapper for plugin_recvmsg.
|
||||
func Recvmsg(handle uint32, iovs []syscall.Iovec, addr, control []byte, flags int) (int64, int, int, int) {
|
||||
lenAddr := len(addr)
|
||||
lenCtl := len(control)
|
||||
sysflags := flags | syscall.MSG_DONTWAIT
|
||||
|
||||
if len(iovs) == 0 {
|
||||
return 0, lenAddr, lenCtl, 0
|
||||
}
|
||||
|
||||
var ptrAddr, ptrCtl *byte
|
||||
if lenAddr > 0 {
|
||||
ptrAddr = &addr[0]
|
||||
}
|
||||
|
||||
if lenCtl > 0 {
|
||||
ptrCtl = &control[0]
|
||||
}
|
||||
|
||||
msg := syscall.Msghdr{
|
||||
Iov: &iovs[0],
|
||||
Iovlen: uint64(len(iovs)),
|
||||
Name: ptrAddr,
|
||||
Namelen: uint32(lenAddr),
|
||||
Control: ptrCtl,
|
||||
Controllen: uint64(lenCtl),
|
||||
}
|
||||
|
||||
var errno uint64
|
||||
if ret := int64(C.plugin_recvmsg(
|
||||
C.int(handle),
|
||||
(*C.struct_msghdr)(unsafe.Pointer(&msg)),
|
||||
C.int(sysflags),
|
||||
(*C.uint64_t)(unsafe.Pointer(&errno)))); ret < 0 {
|
||||
return -int64(errno), lenAddr, lenCtl, 0
|
||||
} else {
|
||||
return ret, int(msg.Namelen), int(msg.Controllen), int(msg.Flags)
|
||||
}
|
||||
}
|
||||
|
||||
// Write works as a CGO wrapper for plugin_write.
|
||||
func Write(handle uint32, buf uintptr, count int) int64 {
|
||||
var errno uint64
|
||||
return convertRetVal(
|
||||
int64(C.plugin_write(
|
||||
C.int(handle),
|
||||
unsafe.Pointer(buf),
|
||||
C.size_t(count),
|
||||
(*C.uint64_t)(unsafe.Pointer(&errno)))),
|
||||
errno)
|
||||
}
|
||||
|
||||
// Writev works as a CGO wrapper for plugin_writev.
|
||||
func Writev(handle uint32, iovs []syscall.Iovec) int64 {
|
||||
var errno uint64
|
||||
if len(iovs) == 0 {
|
||||
return 0
|
||||
}
|
||||
return convertRetVal(
|
||||
int64(C.plugin_writev(
|
||||
C.int(handle),
|
||||
(*C.struct_iovec)(unsafe.Pointer(&iovs[0])),
|
||||
C.int(len(iovs)),
|
||||
(*C.uint64_t)(unsafe.Pointer(&errno)))),
|
||||
errno)
|
||||
}
|
||||
|
||||
// Sendto works as a CGO wrapper for plugin_sendto.
|
||||
func Sendto(handle uint32, buf uintptr, count int, flags int, addr []byte) int64 {
|
||||
var errno uint64
|
||||
return convertRetVal(
|
||||
int64(C.plugin_sendto(
|
||||
C.int(handle),
|
||||
unsafe.Pointer(buf),
|
||||
C.size_t(count),
|
||||
C.int(flags),
|
||||
(*C.struct_sockaddr)(GetPtr(addr)),
|
||||
C.socklen_t(len(addr)),
|
||||
(*C.uint64_t)(unsafe.Pointer(&errno)))),
|
||||
errno)
|
||||
}
|
||||
|
||||
// Sendmsg works as a CGO wrapper for plugin_sendmsg.
|
||||
func Sendmsg(handle uint32, iovs []syscall.Iovec, addr []byte, flags int) int64 {
|
||||
var errno uint64
|
||||
if len(iovs) == 0 {
|
||||
return 0
|
||||
}
|
||||
if len(addr) == 0 {
|
||||
return -linuxerrno.EINVAL
|
||||
}
|
||||
|
||||
msg := syscall.Msghdr{
|
||||
Iov: &iovs[0],
|
||||
Iovlen: uint64(len(iovs)),
|
||||
Name: &addr[0],
|
||||
Namelen: uint32(len(addr)),
|
||||
}
|
||||
return convertRetVal(
|
||||
int64(C.plugin_sendmsg(
|
||||
C.int(handle),
|
||||
(*C.struct_msghdr)(unsafe.Pointer(&msg)),
|
||||
C.int(flags),
|
||||
(*C.uint64_t)(unsafe.Pointer(&errno)))),
|
||||
errno)
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
// Copyright 2024 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.
|
||||
|
||||
//go:build network_plugins
|
||||
// +build network_plugins
|
||||
|
||||
package cgo
|
||||
|
||||
/*
|
||||
#include <stdlib.h>
|
||||
|
||||
// stack initialization operations
|
||||
int plugin_initstack(char *init_str, int *fds, int num);
|
||||
int plugin_preinitstack(int pid, char **init_str_ptr, int **fds, int *num);
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// InitStack implements CGO wrapper for plugin_initstack.
|
||||
func InitStack(initStr string, fds []int) error {
|
||||
cs := C.CString(initStr)
|
||||
defer C.free(unsafe.Pointer(cs))
|
||||
fdNum := len(fds)
|
||||
cfds := make([]C.int, fdNum)
|
||||
for i := 0; i < fdNum; i++ {
|
||||
cfds[i] = (C.int)(fds[i])
|
||||
}
|
||||
|
||||
if ret := C.plugin_initstack(cs, (*C.int)(&cfds[0]), (C.int)(fdNum)); ret != 0 {
|
||||
return fmt.Errorf("failed to init stack, ret = %v", ret)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// PreInitStack implements CGO wrapper for plugin_preinitstack.
|
||||
func PreInitStack(pid int) (string, []int, error) {
|
||||
var (
|
||||
cInitStr *C.char
|
||||
cFdArray *C.int
|
||||
num C.int
|
||||
)
|
||||
|
||||
if ret := C.plugin_preinitstack(
|
||||
C.int(pid),
|
||||
(**C.char)(unsafe.Pointer(&cInitStr)),
|
||||
(**C.int)(unsafe.Pointer(&cFdArray)),
|
||||
(*C.int)(unsafe.Pointer(&num))); ret != 0 {
|
||||
return "", nil, fmt.Errorf("failed to prepare init args for the stack, ret = %v", ret)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
C.free(unsafe.Pointer(cInitStr))
|
||||
C.free(unsafe.Pointer(cFdArray))
|
||||
}()
|
||||
|
||||
initStr := C.GoString(cInitStr)
|
||||
fds := make([]int, int(num))
|
||||
cFds := unsafe.Slice(cFdArray, num)
|
||||
for i := 0; i < int(num); i++ {
|
||||
fds[i] = int(cFds[i])
|
||||
}
|
||||
return initStr, fds, nil
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// 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.
|
||||
|
||||
//go:build network_plugins
|
||||
// +build network_plugins
|
||||
|
||||
package cgo
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// GetPtr gets []byte's start address and converts the address into
|
||||
// unsafe.Pointer that will be used as C pointer.
|
||||
func GetPtr(bs []byte) unsafe.Pointer {
|
||||
if len(bs) == 0 {
|
||||
return nil
|
||||
}
|
||||
return unsafe.Pointer(&bs[0])
|
||||
}
|
||||
|
||||
func convertRetVal(ret int64, errno uint64) int64 {
|
||||
if ret < 0 {
|
||||
return -int64(errno)
|
||||
} else {
|
||||
return ret
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// 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 plugin
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/seccomp"
|
||||
)
|
||||
|
||||
// SeccompFilters defines seccomp allowed rules that are needed by cgo.
|
||||
func SeccompFilters() seccomp.SyscallRules {
|
||||
return seccomp.MakeSyscallRules(map[uintptr]seccomp.SyscallRule{
|
||||
unix.SYS_MMAP: seccomp.PerArg{
|
||||
// allow alloc_seg in DPDK
|
||||
seccomp.AnyValue{},
|
||||
seccomp.AnyValue{},
|
||||
seccomp.AnyValue{},
|
||||
seccomp.EqualTo(
|
||||
unix.MAP_SHARED |
|
||||
unix.MAP_ANONYMOUS |
|
||||
unix.MAP_FIXED),
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
// 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 plugin provides a set of interfaces to interact with
|
||||
// third-party netstack. It will be used during sandbox network setup when
|
||||
// NetworkType is set as NetworkPlugin.
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"gvisor.dev/gvisor/pkg/sentry/inet"
|
||||
"gvisor.dev/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
// PluginStack defines a set of stack operations to work with a third-party
|
||||
// plugin stack.
|
||||
type PluginStack interface {
|
||||
inet.Stack
|
||||
|
||||
// Init initializes plugin stack.
|
||||
Init(args *InitStackArgs) error
|
||||
|
||||
// PreInit handles prepare steps before initializing plugin stack.
|
||||
// It may include joining namespace, mounting NIC, etc.
|
||||
PreInit(args *PreInitStackArgs) (string, []int, error)
|
||||
}
|
||||
|
||||
// InitStackArgs is a struct that holds arguments needed by PluginStack.Init.
|
||||
type InitStackArgs struct {
|
||||
// InitStr represents arguments needed to initialize plugin stack.
|
||||
InitStr string
|
||||
|
||||
// FDs represents files opened during stack pre-init stage, which will
|
||||
// be used in stack initialization.
|
||||
FDs []int
|
||||
}
|
||||
|
||||
// PreInitStackArgs is a struct that holds arguments needed by
|
||||
// PluginStack.PreInit.
|
||||
type PreInitStackArgs struct {
|
||||
// Pid represents current process that invokes plugin stack
|
||||
// pre-init.
|
||||
Pid int
|
||||
}
|
||||
|
||||
var pluginStack PluginStack
|
||||
|
||||
// RegisterPluginStack registers given stack as plugin stack.
|
||||
func RegisterPluginStack(stack PluginStack) {
|
||||
if pluginStack != nil {
|
||||
panic("called RegisterPluginStack more than once")
|
||||
}
|
||||
pluginStack = stack
|
||||
}
|
||||
|
||||
// GetPluginStack fetches the current registered plugin stack.
|
||||
func GetPluginStack() PluginStack {
|
||||
return pluginStack
|
||||
}
|
||||
|
||||
// EventInfo is a struct that holds information necessary to a socket
|
||||
// notification mechanisms.
|
||||
type EventInfo struct {
|
||||
// Queue is the socket corresponding event queue.
|
||||
Wq *waiter.Queue
|
||||
|
||||
// Mask represents events this socket registered.
|
||||
Mask waiter.EventMask
|
||||
|
||||
// Ready represents events has been currently reported.
|
||||
Ready waiter.EventMask
|
||||
|
||||
// Waiting represents whether there is any waiting event.
|
||||
Waiting bool
|
||||
}
|
||||
|
||||
// PluginNotifier represents a set of operations to handle
|
||||
// plugin network stack's event notification mechanisms.
|
||||
type PluginNotifier interface {
|
||||
// AddFD registers a new socket fd and its corresponding
|
||||
// event notification info into the global fdMap.
|
||||
AddFD(fd uint32, eventinfo *EventInfo) error
|
||||
|
||||
// RemoveFD unregisters a socket fd and its corresponding
|
||||
// event notification info from the global fdMap.
|
||||
RemoveFD(fd uint32)
|
||||
|
||||
// UpdateFD updates the set of events the socket fd needs
|
||||
// to be notified on.
|
||||
UpdateFD(fd uint32) error
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
load("//tools:defs.bzl", "go_library")
|
||||
|
||||
package(
|
||||
default_applicable_licenses = ["//:license"],
|
||||
licenses = ["notice"],
|
||||
)
|
||||
|
||||
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() any {
|
||||
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
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user