mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Add support for receiving TOS and TCLASS control messages in hostinet.
This involves allowing getsockopt/setsockopt for the corresponding socket options, as well as allowing hostinet to process control messages received from the actual recvmsg syscall. PiperOrigin-RevId: 282851425
This commit is contained in:
@@ -422,6 +422,15 @@ type ControlMessageRights []int32
|
||||
// ControlMessageRights.
|
||||
const SizeOfControlMessageRight = 4
|
||||
|
||||
// SizeOfControlMessageInq is the size of a TCP_INQ control message.
|
||||
const SizeOfControlMessageInq = 4
|
||||
|
||||
// SizeOfControlMessageTOS is the size of an IP_TOS control message.
|
||||
const SizeOfControlMessageTOS = 1
|
||||
|
||||
// SizeOfControlMessageTClass is the size of an IPV6_TCLASS control message.
|
||||
const SizeOfControlMessageTClass = 4
|
||||
|
||||
// SCM_MAX_FD is the maximum number of FDs accepted in a single sendmsg call.
|
||||
// From net/scm.h.
|
||||
const SCM_MAX_FD = 253
|
||||
|
||||
@@ -320,11 +320,33 @@ func PackInq(t *kernel.Task, inq int32, buf []byte) []byte {
|
||||
buf,
|
||||
linux.SOL_TCP,
|
||||
linux.TCP_INQ,
|
||||
4,
|
||||
t.Arch().Width(),
|
||||
inq,
|
||||
)
|
||||
}
|
||||
|
||||
// PackTOS packs an IP_TOS socket control message.
|
||||
func PackTOS(t *kernel.Task, tos int8, buf []byte) []byte {
|
||||
return putCmsgStruct(
|
||||
buf,
|
||||
linux.SOL_IP,
|
||||
linux.IP_TOS,
|
||||
t.Arch().Width(),
|
||||
tos,
|
||||
)
|
||||
}
|
||||
|
||||
// PackTClass packs an IPV6_TCLASS socket control message.
|
||||
func PackTClass(t *kernel.Task, tClass int32, buf []byte) []byte {
|
||||
return putCmsgStruct(
|
||||
buf,
|
||||
linux.SOL_IPV6,
|
||||
linux.IPV6_TCLASS,
|
||||
t.Arch().Width(),
|
||||
tClass,
|
||||
)
|
||||
}
|
||||
|
||||
// Parse parses a raw socket control message into portable objects.
|
||||
func Parse(t *kernel.Task, socketOrEndpoint interface{}, buf []byte) (transport.ControlMessages, error) {
|
||||
var (
|
||||
|
||||
@@ -34,5 +34,6 @@ go_library(
|
||||
"//pkg/syserror",
|
||||
"//pkg/tcpip/stack",
|
||||
"//pkg/waiter",
|
||||
"@org_golang_x_sys//unix:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/binary"
|
||||
"gvisor.dev/gvisor/pkg/fdnotifier"
|
||||
@@ -41,6 +42,10 @@ const (
|
||||
// sizeofSockaddr is the size in bytes of the largest sockaddr type
|
||||
// supported by this package.
|
||||
sizeofSockaddr = syscall.SizeofSockaddrInet6 // sizeof(sockaddr_in6) > sizeof(sockaddr_in)
|
||||
|
||||
// maxControlLen is the maximum size of a control message buffer used in a
|
||||
// recvmsg syscall.
|
||||
maxControlLen = 1024
|
||||
)
|
||||
|
||||
// socketOperations implements fs.FileOperations and socket.Socket for a socket
|
||||
@@ -281,26 +286,32 @@ func (s *socketOperations) GetSockOpt(t *kernel.Task, level int, name int, outPt
|
||||
// Whitelist options and constrain option length.
|
||||
var optlen int
|
||||
switch level {
|
||||
case syscall.SOL_IPV6:
|
||||
case linux.SOL_IP:
|
||||
switch name {
|
||||
case syscall.IPV6_V6ONLY:
|
||||
case linux.IP_RECVTOS:
|
||||
optlen = sizeofInt32
|
||||
}
|
||||
case syscall.SOL_SOCKET:
|
||||
case linux.SOL_IPV6:
|
||||
switch name {
|
||||
case syscall.SO_ERROR, syscall.SO_KEEPALIVE, syscall.SO_SNDBUF, syscall.SO_RCVBUF, syscall.SO_REUSEADDR:
|
||||
case linux.IPV6_RECVTCLASS, linux.IPV6_V6ONLY:
|
||||
optlen = sizeofInt32
|
||||
case syscall.SO_LINGER:
|
||||
}
|
||||
case linux.SOL_SOCKET:
|
||||
switch name {
|
||||
case linux.SO_ERROR, linux.SO_KEEPALIVE, linux.SO_SNDBUF, linux.SO_RCVBUF, linux.SO_REUSEADDR:
|
||||
optlen = sizeofInt32
|
||||
case linux.SO_LINGER:
|
||||
optlen = syscall.SizeofLinger
|
||||
}
|
||||
case syscall.SOL_TCP:
|
||||
case linux.SOL_TCP:
|
||||
switch name {
|
||||
case syscall.TCP_NODELAY:
|
||||
case linux.TCP_NODELAY:
|
||||
optlen = sizeofInt32
|
||||
case syscall.TCP_INFO:
|
||||
case linux.TCP_INFO:
|
||||
optlen = int(linux.SizeOfTCPInfo)
|
||||
}
|
||||
}
|
||||
|
||||
if optlen == 0 {
|
||||
return nil, syserr.ErrProtocolNotAvailable // ENOPROTOOPT
|
||||
}
|
||||
@@ -320,19 +331,24 @@ func (s *socketOperations) SetSockOpt(t *kernel.Task, level int, name int, opt [
|
||||
// Whitelist options and constrain option length.
|
||||
var optlen int
|
||||
switch level {
|
||||
case syscall.SOL_IPV6:
|
||||
case linux.SOL_IP:
|
||||
switch name {
|
||||
case syscall.IPV6_V6ONLY:
|
||||
case linux.IP_RECVTOS:
|
||||
optlen = sizeofInt32
|
||||
}
|
||||
case syscall.SOL_SOCKET:
|
||||
case linux.SOL_IPV6:
|
||||
switch name {
|
||||
case syscall.SO_SNDBUF, syscall.SO_RCVBUF, syscall.SO_REUSEADDR:
|
||||
case linux.IPV6_RECVTCLASS, linux.IPV6_V6ONLY:
|
||||
optlen = sizeofInt32
|
||||
}
|
||||
case syscall.SOL_TCP:
|
||||
case linux.SOL_SOCKET:
|
||||
switch name {
|
||||
case syscall.TCP_NODELAY:
|
||||
case linux.SO_SNDBUF, linux.SO_RCVBUF, linux.SO_REUSEADDR:
|
||||
optlen = sizeofInt32
|
||||
}
|
||||
case linux.SOL_TCP:
|
||||
switch name {
|
||||
case linux.TCP_NODELAY:
|
||||
optlen = sizeofInt32
|
||||
}
|
||||
}
|
||||
@@ -354,11 +370,11 @@ func (s *socketOperations) SetSockOpt(t *kernel.Task, level int, name int, opt [
|
||||
}
|
||||
|
||||
// RecvMsg implements socket.Socket.RecvMsg.
|
||||
func (s *socketOperations) RecvMsg(t *kernel.Task, dst usermem.IOSequence, flags int, haveDeadline bool, deadline ktime.Time, senderRequested bool, controlDataLen uint64) (int, int, linux.SockAddr, uint32, socket.ControlMessages, *syserr.Error) {
|
||||
func (s *socketOperations) RecvMsg(t *kernel.Task, dst usermem.IOSequence, flags int, haveDeadline bool, deadline ktime.Time, senderRequested bool, controlLen uint64) (int, int, linux.SockAddr, uint32, socket.ControlMessages, *syserr.Error) {
|
||||
// Whitelist flags.
|
||||
//
|
||||
// FIXME(jamieliu): We can't support MSG_ERRQUEUE because it uses ancillary
|
||||
// messages that netstack/tcpip/transport/unix doesn't understand. Kill the
|
||||
// messages that gvisor/pkg/tcpip/transport/unix doesn't understand. Kill the
|
||||
// Socket interface's dependence on netstack.
|
||||
if flags&^(syscall.MSG_DONTWAIT|syscall.MSG_PEEK|syscall.MSG_TRUNC) != 0 {
|
||||
return 0, 0, nil, 0, socket.ControlMessages{}, syserr.ErrInvalidArgument
|
||||
@@ -370,6 +386,7 @@ func (s *socketOperations) RecvMsg(t *kernel.Task, dst usermem.IOSequence, flags
|
||||
senderAddrBuf = make([]byte, sizeofSockaddr)
|
||||
}
|
||||
|
||||
var controlBuf []byte
|
||||
var msgFlags int
|
||||
|
||||
recvmsgToBlocks := safemem.ReaderFunc(func(dsts safemem.BlockSeq) (uint64, error) {
|
||||
@@ -384,11 +401,6 @@ func (s *socketOperations) RecvMsg(t *kernel.Task, dst usermem.IOSequence, flags
|
||||
// We always do a non-blocking recv*().
|
||||
sysflags := flags | syscall.MSG_DONTWAIT
|
||||
|
||||
if dsts.NumBlocks() == 1 {
|
||||
// Skip allocating []syscall.Iovec.
|
||||
return recvfrom(s.fd, dsts.Head().ToSlice(), sysflags, &senderAddrBuf)
|
||||
}
|
||||
|
||||
iovs := iovecsFromBlockSeq(dsts)
|
||||
msg := syscall.Msghdr{
|
||||
Iov: &iovs[0],
|
||||
@@ -398,12 +410,18 @@ func (s *socketOperations) RecvMsg(t *kernel.Task, dst usermem.IOSequence, flags
|
||||
msg.Name = &senderAddrBuf[0]
|
||||
msg.Namelen = uint32(len(senderAddrBuf))
|
||||
}
|
||||
if controlLen > 0 {
|
||||
controlBuf = make([]byte, maxControlLen)
|
||||
msg.Control = &controlBuf[0]
|
||||
msg.Controllen = maxControlLen
|
||||
}
|
||||
n, err := recvmsg(s.fd, &msg, sysflags)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
senderAddrBuf = senderAddrBuf[:msg.Namelen]
|
||||
msgFlags = int(msg.Flags)
|
||||
controlLen = uint64(msg.Controllen)
|
||||
return n, nil
|
||||
})
|
||||
|
||||
@@ -429,14 +447,38 @@ func (s *socketOperations) RecvMsg(t *kernel.Task, dst usermem.IOSequence, flags
|
||||
n, err = dst.CopyOutFrom(t, recvmsgToBlocks)
|
||||
}
|
||||
}
|
||||
|
||||
// We don't allow control messages.
|
||||
msgFlags &^= linux.MSG_CTRUNC
|
||||
if err != nil {
|
||||
return 0, 0, nil, 0, socket.ControlMessages{}, syserr.FromError(err)
|
||||
}
|
||||
|
||||
if senderRequested {
|
||||
senderAddr = socket.UnmarshalSockAddr(s.family, senderAddrBuf)
|
||||
}
|
||||
return int(n), msgFlags, senderAddr, uint32(len(senderAddrBuf)), socket.ControlMessages{}, syserr.FromError(err)
|
||||
|
||||
unixControlMessages, err := unix.ParseSocketControlMessage(controlBuf[:controlLen])
|
||||
if err != nil {
|
||||
return 0, 0, nil, 0, socket.ControlMessages{}, syserr.FromError(err)
|
||||
}
|
||||
|
||||
controlMessages := socket.ControlMessages{}
|
||||
for _, unixCmsg := range unixControlMessages {
|
||||
switch unixCmsg.Header.Level {
|
||||
case syscall.SOL_IP:
|
||||
switch unixCmsg.Header.Type {
|
||||
case syscall.IP_TOS:
|
||||
controlMessages.IP.HasTOS = true
|
||||
binary.Unmarshal(unixCmsg.Data[:linux.SizeOfControlMessageTOS], usermem.ByteOrder, &controlMessages.IP.TOS)
|
||||
}
|
||||
case syscall.SOL_IPV6:
|
||||
switch unixCmsg.Header.Type {
|
||||
case syscall.IPV6_TCLASS:
|
||||
controlMessages.IP.HasTClass = true
|
||||
binary.Unmarshal(unixCmsg.Data[:linux.SizeOfControlMessageTClass], usermem.ByteOrder, &controlMessages.IP.TClass)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return int(n), msgFlags, senderAddr, uint32(len(senderAddrBuf)), controlMessages, nil
|
||||
}
|
||||
|
||||
// SendMsg implements socket.Socket.SendMsg.
|
||||
|
||||
@@ -802,6 +802,14 @@ func recvSingleMsg(t *kernel.Task, s socket.Socket, msgPtr usermem.Addr, flags i
|
||||
controlData = control.PackInq(t, cms.IP.Inq, controlData)
|
||||
}
|
||||
|
||||
if cms.IP.HasTOS {
|
||||
controlData = control.PackTOS(t, cms.IP.TOS, controlData)
|
||||
}
|
||||
|
||||
if cms.IP.HasTClass {
|
||||
controlData = control.PackTClass(t, cms.IP.TClass, controlData)
|
||||
}
|
||||
|
||||
if cms.Unix.Rights != nil {
|
||||
controlData, mflags = control.PackRights(t, cms.Unix.Rights.(control.SCMRights), flags&linux.MSG_CMSG_CLOEXEC != 0, controlData, mflags)
|
||||
}
|
||||
|
||||
+13
-1
@@ -308,7 +308,7 @@ type ControlMessages struct {
|
||||
// HasTimestamp indicates whether Timestamp is valid/set.
|
||||
HasTimestamp bool
|
||||
|
||||
// Timestamp is the time (in ns) that the last packed used to create
|
||||
// Timestamp is the time (in ns) that the last packet used to create
|
||||
// the read data was received.
|
||||
Timestamp int64
|
||||
|
||||
@@ -317,6 +317,18 @@ type ControlMessages struct {
|
||||
|
||||
// Inq is the number of bytes ready to be received.
|
||||
Inq int32
|
||||
|
||||
// HasTOS indicates whether Tos is valid/set.
|
||||
HasTOS bool
|
||||
|
||||
// TOS is the IPv4 type of service of the associated packet.
|
||||
TOS int8
|
||||
|
||||
// HasTClass indicates whether Tclass is valid/set.
|
||||
HasTClass bool
|
||||
|
||||
// Tclass is the IPv6 traffic class of the associated packet.
|
||||
TClass int32
|
||||
}
|
||||
|
||||
// Endpoint is the interface implemented by transport protocols (e.g., tcp, udp)
|
||||
|
||||
@@ -134,11 +134,6 @@ var allowedSyscalls = seccomp.SyscallRules{
|
||||
seccomp.AllowValue(syscall.SOL_SOCKET),
|
||||
seccomp.AllowValue(syscall.SO_SNDBUF),
|
||||
},
|
||||
{
|
||||
seccomp.AllowAny{},
|
||||
seccomp.AllowValue(syscall.SOL_SOCKET),
|
||||
seccomp.AllowValue(syscall.SO_REUSEADDR),
|
||||
},
|
||||
},
|
||||
syscall.SYS_GETTID: {},
|
||||
syscall.SYS_GETTIMEOFDAY: {},
|
||||
@@ -315,6 +310,16 @@ func hostInetFilters() seccomp.SyscallRules {
|
||||
syscall.SYS_GETPEERNAME: {},
|
||||
syscall.SYS_GETSOCKNAME: {},
|
||||
syscall.SYS_GETSOCKOPT: []seccomp.Rule{
|
||||
{
|
||||
seccomp.AllowAny{},
|
||||
seccomp.AllowValue(syscall.SOL_IP),
|
||||
seccomp.AllowValue(syscall.IP_RECVTOS),
|
||||
},
|
||||
{
|
||||
seccomp.AllowAny{},
|
||||
seccomp.AllowValue(syscall.SOL_IPV6),
|
||||
seccomp.AllowValue(syscall.IPV6_RECVTCLASS),
|
||||
},
|
||||
{
|
||||
seccomp.AllowAny{},
|
||||
seccomp.AllowValue(syscall.SOL_IPV6),
|
||||
@@ -418,6 +423,20 @@ func hostInetFilters() seccomp.SyscallRules {
|
||||
seccomp.AllowAny{},
|
||||
seccomp.AllowValue(4),
|
||||
},
|
||||
{
|
||||
seccomp.AllowAny{},
|
||||
seccomp.AllowValue(syscall.SOL_IP),
|
||||
seccomp.AllowValue(syscall.IP_RECVTOS),
|
||||
seccomp.AllowAny{},
|
||||
seccomp.AllowValue(4),
|
||||
},
|
||||
{
|
||||
seccomp.AllowAny{},
|
||||
seccomp.AllowValue(syscall.SOL_IPV6),
|
||||
seccomp.AllowValue(syscall.IPV6_RECVTCLASS),
|
||||
seccomp.AllowAny{},
|
||||
seccomp.AllowValue(4),
|
||||
},
|
||||
},
|
||||
syscall.SYS_SHUTDOWN: []seccomp.Rule{
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user