Skip to content

Commit

Permalink
Support plugin network stack
Browse files Browse the repository at this point in the history
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=cgo-enable runsc:runsc-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 #9266

Co-developed-by: Tianyu Zhou <wentong.zty@antgroup.com>
Signed-off-by: Anqi Shen <amy.saq@antgroup.com>
FUTURE_COPYBARA_INTEGRATE_REVIEW=#9551 from amysaq2023:support-external-stack 56f2530
PiperOrigin-RevId: 677140616
  • Loading branch information
amysaq2023 authored and gvisor-bot committed Sep 23, 2024
1 parent ffb3b11 commit ef642b2
Show file tree
Hide file tree
Showing 48 changed files with 2,738 additions and 8 deletions.
3 changes: 3 additions & 0 deletions .bazelrc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions .buildkite/pipeline.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -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"

Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ go_path(
"//tools/checklocks",

# Packages that are not dependencies of the above.
"//pkg/sentry/socket/plugin/stack",
"//pkg/sentry/kernel/memevent",
"//pkg/tcpip/adapters/gonet",
"//pkg/tcpip/faketime",
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
7 changes: 7 additions & 0 deletions WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
Expand Down Expand Up @@ -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",
)
3 changes: 3 additions & 0 deletions pkg/abi/linux/netdevice.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
//
Expand Down
18 changes: 18 additions & 0 deletions pkg/sentry/socket/plugin/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
load("//tools:defs.bzl", "go_library")

package(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",
],
)
37 changes: 37 additions & 0 deletions pkg/sentry/socket/plugin/cgo/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
load("//tools:defs.bzl", "go_library")

package(licenses = ["notice"])

config_setting(
name = "network_plugins",
values = {"define": "network_plugins=true"},
)

go_library(
name = "cgo",
srcs = [
"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",
],
)
112 changes: 112 additions & 0 deletions pkg/sentry/socket/plugin/cgo/nocgo_stub_unsafe.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// 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 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

import (
"syscall"
"unsafe"
)

func GetPtr(bs []byte) unsafe.Pointer {
panic("unimplemented")
}
func EpollCreate() int {
panic("unimplemented")
}
func EpollCtl(epfd int32, op int, handle, events uint32) {
panic("unimplemented")
}

func EpollWait(epfd int32, events []syscall.EpollEvent, n int, us int) int {
panic("unimplemented")
}
func Socket(domain, skType, protocol int) int64 {
panic("unimplemented")
}
func Bind(handle uint32, sa []byte) int64 {
panic("unimplemented")
}
func Listen(handle uint32, backlog int) int64 {
panic("unimplemented")
}
func Accept(handle uint32, addrPtr *byte, lenPtr *uint32) int64 {
panic("unimplemented")
}
func Ioctl(handle uint32, cmd uint32, buf []byte) int64 {
panic("unimplemented")
}
func Connect(handle uint32, addr []byte) int64 {
panic("unimplemented")
}
func Getsockopt(handle uint32, l int, n int, val []byte, s int) (int64, int) {
panic("unimplemented")
}
func Setsockopt(handle uint32, l int, n int, val []byte) int64 {
panic("unimplemented")
}
func Shutdown(handle uint32, how int) int64 {
panic("unimplemented")
}
func Close(handle uint32) {
panic("unimplemented")
}
func Getsockname(handle uint32, addr []byte, addrlen *uint32) int64 {
panic("unimplemented")
}
func GetPeername(handle uint32, addr []byte, addrlen *uint32) int64 {
panic("unimplemented")
}
func Readiness(handle uint32, mask uint64) int64 {
panic("unimplemented")
}
func Read(handle uint32, buf uintptr, count int) int64 {
panic("unimplemented")
}
func Readv(handle uint32, iovs []syscall.Iovec) int64 {
panic("unimplemented")
}
func Recvfrom(handle uint32, buf, addr []byte, flags int) (int64, int) {
panic("unimplemented")
}
func Recvmsg(handle uint32, iovs []syscall.Iovec, addr, control []byte, flags int) (int64, int, int, int) {
panic("unimplemented")
}
func Write(handle uint32, buf uintptr, count int) int64 {
panic("unimplemented")
}
func Writev(handle uint32, iovs []syscall.Iovec) int64 {
panic("unimplemented")
}
func Sendto(handle uint32, buf uintptr, count int, flags int, addr []byte) int64 {
panic("unimplemented")
}
func Sendmsg(handle uint32, iovs []syscall.Iovec, addr []byte, flags int) int64 {
panic("unimplemented")
}
func InitStack(initStr string, fds []int) error {
panic("unimplemented")
}
func PreInitStack(pid int) (string, []int, error) {
panic("unimplemented")
}
Loading

0 comments on commit ef642b2

Please sign in to comment.