Files
2024-09-23 14:12:32 -07:00

423 lines
12 KiB
Go

// 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)
}