mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Support sending IP_TOS and IPV6_TCLASS control messages with hostinet sockets.
There are two potential ways of sending a TOS byte with outgoing packets: including a control message in sendmsg, or setting the IP_TOS/IPV6_TCLASS socket options (for IPV4 and IPV6 respectively). This change lets hostinet support the former. PiperOrigin-RevId: 283346737
This commit is contained in:
@@ -17,6 +17,7 @@ go_library(
|
||||
"//pkg/sentry/fs",
|
||||
"//pkg/sentry/kernel",
|
||||
"//pkg/sentry/kernel/auth",
|
||||
"//pkg/sentry/socket",
|
||||
"//pkg/sentry/socket/unix/transport",
|
||||
"//pkg/sentry/usermem",
|
||||
"//pkg/syserror",
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/sentry/fs"
|
||||
"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/unix/transport"
|
||||
"gvisor.dev/gvisor/pkg/sentry/usermem"
|
||||
"gvisor.dev/gvisor/pkg/syserror"
|
||||
@@ -347,30 +348,63 @@ func PackTClass(t *kernel.Task, tClass int32, buf []byte) []byte {
|
||||
)
|
||||
}
|
||||
|
||||
func addSpaceForCmsg(cmsgDataLen int, buf []byte) []byte {
|
||||
newBuf := make([]byte, 0, len(buf)+linux.SizeOfControlMessageHeader+cmsgDataLen)
|
||||
return append(newBuf, buf...)
|
||||
}
|
||||
|
||||
// PackControlMessages converts the given ControlMessages struct into a buffer.
|
||||
// We skip control messages specific to Unix domain sockets.
|
||||
func PackControlMessages(t *kernel.Task, cmsgs socket.ControlMessages) []byte {
|
||||
var buf []byte
|
||||
// The use of t.Arch().Width() is analogous to Linux's use of sizeof(long) in
|
||||
// CMSG_ALIGN.
|
||||
width := t.Arch().Width()
|
||||
|
||||
if cmsgs.IP.HasTimestamp {
|
||||
buf = addSpaceForCmsg(int(width), buf)
|
||||
buf = PackTimestamp(t, cmsgs.IP.Timestamp, buf)
|
||||
}
|
||||
|
||||
if cmsgs.IP.HasInq {
|
||||
// In Linux, TCP_CM_INQ is added after SO_TIMESTAMP.
|
||||
buf = addSpaceForCmsg(AlignUp(linux.SizeOfControlMessageInq, width), buf)
|
||||
buf = PackInq(t, cmsgs.IP.Inq, buf)
|
||||
}
|
||||
|
||||
if cmsgs.IP.HasTOS {
|
||||
buf = addSpaceForCmsg(AlignUp(linux.SizeOfControlMessageTOS, width), buf)
|
||||
buf = PackTOS(t, cmsgs.IP.TOS, buf)
|
||||
}
|
||||
|
||||
if cmsgs.IP.HasTClass {
|
||||
buf = addSpaceForCmsg(AlignUp(linux.SizeOfControlMessageTClass, width), buf)
|
||||
buf = PackTClass(t, cmsgs.IP.TClass, buf)
|
||||
}
|
||||
|
||||
return buf
|
||||
}
|
||||
|
||||
// Parse parses a raw socket control message into portable objects.
|
||||
func Parse(t *kernel.Task, socketOrEndpoint interface{}, buf []byte) (transport.ControlMessages, error) {
|
||||
func Parse(t *kernel.Task, socketOrEndpoint interface{}, buf []byte) (socket.ControlMessages, error) {
|
||||
var (
|
||||
fds linux.ControlMessageRights
|
||||
haveCreds bool
|
||||
creds linux.ControlMessageCredentials
|
||||
cmsgs socket.ControlMessages
|
||||
fds linux.ControlMessageRights
|
||||
)
|
||||
|
||||
for i := 0; i < len(buf); {
|
||||
if i+linux.SizeOfControlMessageHeader > len(buf) {
|
||||
return transport.ControlMessages{}, syserror.EINVAL
|
||||
return cmsgs, syserror.EINVAL
|
||||
}
|
||||
|
||||
var h linux.ControlMessageHeader
|
||||
binary.Unmarshal(buf[i:i+linux.SizeOfControlMessageHeader], usermem.ByteOrder, &h)
|
||||
|
||||
if h.Length < uint64(linux.SizeOfControlMessageHeader) {
|
||||
return transport.ControlMessages{}, syserror.EINVAL
|
||||
return socket.ControlMessages{}, syserror.EINVAL
|
||||
}
|
||||
if h.Length > uint64(len(buf)-i) {
|
||||
return transport.ControlMessages{}, syserror.EINVAL
|
||||
}
|
||||
if h.Level != linux.SOL_SOCKET {
|
||||
return transport.ControlMessages{}, syserror.EINVAL
|
||||
return socket.ControlMessages{}, syserror.EINVAL
|
||||
}
|
||||
|
||||
i += linux.SizeOfControlMessageHeader
|
||||
@@ -380,59 +414,79 @@ func Parse(t *kernel.Task, socketOrEndpoint interface{}, buf []byte) (transport.
|
||||
// sizeof(long) in CMSG_ALIGN.
|
||||
width := t.Arch().Width()
|
||||
|
||||
switch h.Type {
|
||||
case linux.SCM_RIGHTS:
|
||||
rightsSize := AlignDown(length, linux.SizeOfControlMessageRight)
|
||||
numRights := rightsSize / linux.SizeOfControlMessageRight
|
||||
switch h.Level {
|
||||
case linux.SOL_SOCKET:
|
||||
switch h.Type {
|
||||
case linux.SCM_RIGHTS:
|
||||
rightsSize := AlignDown(length, linux.SizeOfControlMessageRight)
|
||||
numRights := rightsSize / linux.SizeOfControlMessageRight
|
||||
|
||||
if len(fds)+numRights > linux.SCM_MAX_FD {
|
||||
return transport.ControlMessages{}, syserror.EINVAL
|
||||
if len(fds)+numRights > linux.SCM_MAX_FD {
|
||||
return socket.ControlMessages{}, syserror.EINVAL
|
||||
}
|
||||
|
||||
for j := i; j < i+rightsSize; j += linux.SizeOfControlMessageRight {
|
||||
fds = append(fds, int32(usermem.ByteOrder.Uint32(buf[j:j+linux.SizeOfControlMessageRight])))
|
||||
}
|
||||
|
||||
i += AlignUp(length, width)
|
||||
|
||||
case linux.SCM_CREDENTIALS:
|
||||
if length < linux.SizeOfControlMessageCredentials {
|
||||
return socket.ControlMessages{}, syserror.EINVAL
|
||||
}
|
||||
|
||||
var creds linux.ControlMessageCredentials
|
||||
binary.Unmarshal(buf[i:i+linux.SizeOfControlMessageCredentials], usermem.ByteOrder, &creds)
|
||||
scmCreds, err := NewSCMCredentials(t, creds)
|
||||
if err != nil {
|
||||
return socket.ControlMessages{}, err
|
||||
}
|
||||
cmsgs.Unix.Credentials = scmCreds
|
||||
i += AlignUp(length, width)
|
||||
|
||||
default:
|
||||
// Unknown message type.
|
||||
return socket.ControlMessages{}, syserror.EINVAL
|
||||
}
|
||||
case linux.SOL_IP:
|
||||
switch h.Type {
|
||||
case linux.IP_TOS:
|
||||
cmsgs.IP.HasTOS = true
|
||||
binary.Unmarshal(buf[i:i+linux.SizeOfControlMessageTOS], usermem.ByteOrder, &cmsgs.IP.TOS)
|
||||
i += AlignUp(length, width)
|
||||
|
||||
for j := i; j < i+rightsSize; j += linux.SizeOfControlMessageRight {
|
||||
fds = append(fds, int32(usermem.ByteOrder.Uint32(buf[j:j+linux.SizeOfControlMessageRight])))
|
||||
default:
|
||||
return socket.ControlMessages{}, syserror.EINVAL
|
||||
}
|
||||
case linux.SOL_IPV6:
|
||||
switch h.Type {
|
||||
case linux.IPV6_TCLASS:
|
||||
cmsgs.IP.HasTClass = true
|
||||
binary.Unmarshal(buf[i:i+linux.SizeOfControlMessageTClass], usermem.ByteOrder, &cmsgs.IP.TClass)
|
||||
i += AlignUp(length, width)
|
||||
|
||||
i += AlignUp(length, width)
|
||||
|
||||
case linux.SCM_CREDENTIALS:
|
||||
if length < linux.SizeOfControlMessageCredentials {
|
||||
return transport.ControlMessages{}, syserror.EINVAL
|
||||
default:
|
||||
return socket.ControlMessages{}, syserror.EINVAL
|
||||
}
|
||||
|
||||
binary.Unmarshal(buf[i:i+linux.SizeOfControlMessageCredentials], usermem.ByteOrder, &creds)
|
||||
haveCreds = true
|
||||
i += AlignUp(length, width)
|
||||
|
||||
default:
|
||||
// Unknown message type.
|
||||
return transport.ControlMessages{}, syserror.EINVAL
|
||||
return socket.ControlMessages{}, syserror.EINVAL
|
||||
}
|
||||
}
|
||||
|
||||
var credentials SCMCredentials
|
||||
if haveCreds {
|
||||
var err error
|
||||
if credentials, err = NewSCMCredentials(t, creds); err != nil {
|
||||
return transport.ControlMessages{}, err
|
||||
}
|
||||
} else {
|
||||
credentials = makeCreds(t, socketOrEndpoint)
|
||||
if cmsgs.Unix.Credentials == nil {
|
||||
cmsgs.Unix.Credentials = makeCreds(t, socketOrEndpoint)
|
||||
}
|
||||
|
||||
var rights SCMRights
|
||||
if len(fds) > 0 {
|
||||
var err error
|
||||
if rights, err = NewSCMRights(t, fds); err != nil {
|
||||
return transport.ControlMessages{}, err
|
||||
rights, err := NewSCMRights(t, fds)
|
||||
if err != nil {
|
||||
return socket.ControlMessages{}, err
|
||||
}
|
||||
cmsgs.Unix.Rights = rights
|
||||
}
|
||||
|
||||
if credentials == nil && rights == nil {
|
||||
return transport.ControlMessages{}, nil
|
||||
}
|
||||
|
||||
return transport.ControlMessages{Credentials: credentials, Rights: rights}, nil
|
||||
return cmsgs, nil
|
||||
}
|
||||
|
||||
func makeCreds(t *kernel.Task, socketOrEndpoint interface{}) SCMCredentials {
|
||||
|
||||
@@ -29,6 +29,7 @@ go_library(
|
||||
"//pkg/sentry/kernel/time",
|
||||
"//pkg/sentry/safemem",
|
||||
"//pkg/sentry/socket",
|
||||
"//pkg/sentry/socket/control",
|
||||
"//pkg/sentry/usermem",
|
||||
"//pkg/syserr",
|
||||
"//pkg/syserror",
|
||||
|
||||
@@ -30,6 +30,7 @@ import (
|
||||
ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
|
||||
"gvisor.dev/gvisor/pkg/sentry/safemem"
|
||||
"gvisor.dev/gvisor/pkg/sentry/socket"
|
||||
"gvisor.dev/gvisor/pkg/sentry/socket/control"
|
||||
"gvisor.dev/gvisor/pkg/sentry/usermem"
|
||||
"gvisor.dev/gvisor/pkg/syserr"
|
||||
"gvisor.dev/gvisor/pkg/syserror"
|
||||
@@ -488,6 +489,7 @@ func (s *socketOperations) SendMsg(t *kernel.Task, src usermem.IOSequence, to []
|
||||
return 0, syserr.ErrInvalidArgument
|
||||
}
|
||||
|
||||
controlBuf := control.PackControlMessages(t, controlMessages)
|
||||
sendmsgFromBlocks := safemem.WriterFunc(func(srcs safemem.BlockSeq) (uint64, error) {
|
||||
// Refuse to do anything if any part of src.Addrs was unusable.
|
||||
if uint64(src.NumBytes()) != srcs.NumBytes() {
|
||||
@@ -500,7 +502,7 @@ func (s *socketOperations) SendMsg(t *kernel.Task, src usermem.IOSequence, to []
|
||||
// We always do a non-blocking send*().
|
||||
sysflags := flags | syscall.MSG_DONTWAIT
|
||||
|
||||
if srcs.NumBlocks() == 1 {
|
||||
if srcs.NumBlocks() == 1 && len(controlBuf) == 0 {
|
||||
// Skip allocating []syscall.Iovec.
|
||||
src := srcs.Head()
|
||||
n, _, errno := syscall.Syscall6(syscall.SYS_SENDTO, uintptr(s.fd), src.Addr(), uintptr(src.Len()), uintptr(sysflags), uintptr(firstBytePtr(to)), uintptr(len(to)))
|
||||
@@ -519,6 +521,10 @@ func (s *socketOperations) SendMsg(t *kernel.Task, src usermem.IOSequence, to []
|
||||
msg.Name = &to[0]
|
||||
msg.Namelen = uint32(len(to))
|
||||
}
|
||||
if len(controlBuf) != 0 {
|
||||
msg.Control = &controlBuf[0]
|
||||
msg.Controllen = uint64(len(controlBuf))
|
||||
}
|
||||
return sendmsg(s.fd, &msg, sysflags)
|
||||
})
|
||||
|
||||
|
||||
@@ -1068,10 +1068,10 @@ func sendSingleMsg(t *kernel.Task, s socket.Socket, file *fs.File, msgPtr userme
|
||||
}
|
||||
|
||||
// Call the syscall implementation.
|
||||
n, e := s.SendMsg(t, src, to, int(flags), haveDeadline, deadline, socket.ControlMessages{Unix: controlMessages})
|
||||
n, e := s.SendMsg(t, src, to, int(flags), haveDeadline, deadline, controlMessages)
|
||||
err = handleIOError(t, n != 0, e.ToError(), kernel.ERESTARTSYS, "sendmsg", file)
|
||||
if err != nil {
|
||||
controlMessages.Release()
|
||||
controlMessages.Unix.Release()
|
||||
}
|
||||
return uintptr(n), err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user