[netstack] Decouple tcpip.ControlMessages from the IP control messges.

tcpip.ControlMessages can not contain Linux specific structures which makes it
painful to convert back and forth from Linux to tcpip back to Linux when passing
around control messages in hostinet and raw sockets.

Now we convert to the Linux version of the control message as soon as we are
out of tcpip.

PiperOrigin-RevId: 347027065
This commit is contained in:
Ayush Ranjan
2020-12-11 10:33:58 -08:00
committed by gVisor bot
parent e0cde3fb87
commit af4afdc0e0
16 changed files with 138 additions and 71 deletions
+2
View File
@@ -448,6 +448,8 @@ type ControlMessageCredentials struct {
// A ControlMessageIPPacketInfo is IP_PKTINFO socket control message.
//
// ControlMessageIPPacketInfo represents struct in_pktinfo from linux/in.h.
//
// +stateify savable
type ControlMessageIPPacketInfo struct {
NIC int32
LocalAddr InetAddr
-1
View File
@@ -23,7 +23,6 @@ go_library(
"//pkg/sentry/socket/unix/transport",
"//pkg/sentry/vfs",
"//pkg/syserror",
"//pkg/tcpip",
"//pkg/usermem",
],
)
+26 -29
View File
@@ -26,7 +26,6 @@ import (
"gvisor.dev/gvisor/pkg/sentry/socket"
"gvisor.dev/gvisor/pkg/sentry/socket/unix/transport"
"gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/usermem"
)
@@ -344,32 +343,32 @@ func PackTClass(t *kernel.Task, tClass uint32, buf []byte) []byte {
}
// PackIPPacketInfo packs an IP_PKTINFO socket control message.
func PackIPPacketInfo(t *kernel.Task, packetInfo tcpip.IPPacketInfo, buf []byte) []byte {
var p linux.ControlMessageIPPacketInfo
p.NIC = int32(packetInfo.NIC)
copy(p.LocalAddr[:], []byte(packetInfo.LocalAddr))
copy(p.DestinationAddr[:], []byte(packetInfo.DestinationAddr))
func PackIPPacketInfo(t *kernel.Task, packetInfo *linux.ControlMessageIPPacketInfo, buf []byte) []byte {
return putCmsgStruct(
buf,
linux.SOL_IP,
linux.IP_PKTINFO,
t.Arch().Width(),
p,
packetInfo,
)
}
// PackOriginalDstAddress packs an IP_RECVORIGINALDSTADDR socket control message.
func PackOriginalDstAddress(t *kernel.Task, family int, originalDstAddress tcpip.FullAddress, buf []byte) []byte {
p, _ := socket.ConvertAddress(family, originalDstAddress)
level := uint32(linux.SOL_IP)
optType := uint32(linux.IP_RECVORIGDSTADDR)
if family == linux.AF_INET6 {
func PackOriginalDstAddress(t *kernel.Task, originalDstAddress linux.SockAddr, buf []byte) []byte {
var level uint32
var optType uint32
switch originalDstAddress.(type) {
case *linux.SockAddrInet:
level = linux.SOL_IP
optType = linux.IP_RECVORIGDSTADDR
case *linux.SockAddrInet6:
level = linux.SOL_IPV6
optType = linux.IPV6_RECVORIGDSTADDR
default:
panic("invalid address type, must be an IP address for IP_RECVORIGINALDSTADDR cmsg")
}
return putCmsgStruct(
buf, level, optType, t.Arch().Width(), p)
buf, level, optType, t.Arch().Width(), originalDstAddress)
}
// PackControlMessages packs control messages into the given buffer.
@@ -378,7 +377,7 @@ func PackOriginalDstAddress(t *kernel.Task, family int, originalDstAddress tcpip
//
// Note that some control messages may be truncated if they do not fit under
// the capacity of buf.
func PackControlMessages(t *kernel.Task, family int, cmsgs socket.ControlMessages, buf []byte) []byte {
func PackControlMessages(t *kernel.Task, cmsgs socket.ControlMessages, buf []byte) []byte {
if cmsgs.IP.HasTimestamp {
buf = PackTimestamp(t, cmsgs.IP.Timestamp, buf)
}
@@ -397,11 +396,11 @@ func PackControlMessages(t *kernel.Task, family int, cmsgs socket.ControlMessage
}
if cmsgs.IP.HasIPPacketInfo {
buf = PackIPPacketInfo(t, cmsgs.IP.PacketInfo, buf)
buf = PackIPPacketInfo(t, &cmsgs.IP.PacketInfo, buf)
}
if cmsgs.IP.HasOriginalDstAddress {
buf = PackOriginalDstAddress(t, family, cmsgs.IP.OriginalDstAddress, buf)
if cmsgs.IP.OriginalDstAddress != nil {
buf = PackOriginalDstAddress(t, cmsgs.IP.OriginalDstAddress, buf)
}
return buf
@@ -433,19 +432,17 @@ func CmsgsSpace(t *kernel.Task, cmsgs socket.ControlMessages) int {
space += cmsgSpace(t, linux.SizeOfControlMessageTClass)
}
if cmsgs.IP.HasIPPacketInfo {
space += cmsgSpace(t, linux.SizeOfControlMessageIPPacketInfo)
}
if cmsgs.IP.OriginalDstAddress != nil {
space += cmsgSpace(t, cmsgs.IP.OriginalDstAddress.SizeBytes())
}
return space
}
// NewIPPacketInfo returns the IPPacketInfo struct.
func NewIPPacketInfo(packetInfo linux.ControlMessageIPPacketInfo) tcpip.IPPacketInfo {
var p tcpip.IPPacketInfo
p.NIC = tcpip.NICID(packetInfo.NIC)
copy([]byte(p.LocalAddr), packetInfo.LocalAddr[:])
copy([]byte(p.DestinationAddr), packetInfo.DestinationAddr[:])
return p
}
// Parse parses a raw socket control message into portable objects.
func Parse(t *kernel.Task, socketOrEndpoint interface{}, buf []byte) (socket.ControlMessages, error) {
var (
@@ -529,7 +526,7 @@ func Parse(t *kernel.Task, socketOrEndpoint interface{}, buf []byte) (socket.Con
var packetInfo linux.ControlMessageIPPacketInfo
binary.Unmarshal(buf[i:i+linux.SizeOfControlMessageIPPacketInfo], usermem.ByteOrder, &packetInfo)
cmsgs.IP.PacketInfo = NewIPPacketInfo(packetInfo)
cmsgs.IP.PacketInfo = packetInfo
i += binary.AlignUp(length, width)
default:
+2 -2
View File
@@ -523,7 +523,7 @@ func (s *socketOpsCommon) RecvMsg(t *kernel.Task, dst usermem.IOSequence, flags
controlMessages.IP.HasIPPacketInfo = true
var packetInfo linux.ControlMessageIPPacketInfo
binary.Unmarshal(unixCmsg.Data[:linux.SizeOfControlMessageIPPacketInfo], usermem.ByteOrder, &packetInfo)
controlMessages.IP.PacketInfo = control.NewIPPacketInfo(packetInfo)
controlMessages.IP.PacketInfo = packetInfo
}
case syscall.SOL_IPV6:
@@ -551,7 +551,7 @@ func (s *socketOpsCommon) SendMsg(t *kernel.Task, src usermem.IOSequence, to []b
}
controlBuf := make([]byte, 0, space)
// PackControlMessages will append up to space bytes to controlBuf.
controlBuf = control.PackControlMessages(t, s.family, controlMessages, controlBuf)
controlBuf = control.PackControlMessages(t, controlMessages, controlBuf)
sendmsgFromBlocks := safemem.WriterFunc(func(srcs safemem.BlockSeq) (uint64, error) {
// Refuse to do anything if any part of src.Addrs was unusable.
+13 -14
View File
@@ -320,7 +320,7 @@ type socketOpsCommon struct {
readView buffer.View
// readCM holds control message information for the last packet read
// from Endpoint.
readCM tcpip.ControlMessages
readCM socket.IPControlMessages
sender tcpip.FullAddress
linkPacketInfo tcpip.LinkPacketInfo
@@ -408,7 +408,7 @@ func (s *socketOpsCommon) fetchReadView() *syserr.Error {
}
s.readView = v
s.readCM = cms
s.readCM = socket.NewIPControlMessages(s.family, cms)
atomic.StoreUint32(&s.readViewHasData, 1)
return nil
@@ -2736,7 +2736,7 @@ func (s *socketOpsCommon) nonBlockingRead(ctx context.Context, dst usermem.IOSeq
// We need to peek beyond the first message.
dst = dst.DropFirst(n)
num, err := dst.CopyOutFrom(ctx, safemem.FromVecReaderFunc{func(dsts [][]byte) (int64, error) {
n, _, err := s.Endpoint.Peek(dsts)
n, err := s.Endpoint.Peek(dsts)
// TODO(b/78348848): Handle peek timestamp.
if err != nil {
return int64(n), syserr.TranslateNetstackError(err).ToError()
@@ -2780,17 +2780,16 @@ func (s *socketOpsCommon) nonBlockingRead(ctx context.Context, dst usermem.IOSeq
func (s *socketOpsCommon) controlMessages() socket.ControlMessages {
return socket.ControlMessages{
IP: tcpip.ControlMessages{
HasTimestamp: s.readCM.HasTimestamp && s.sockOptTimestamp,
Timestamp: s.readCM.Timestamp,
HasTOS: s.readCM.HasTOS,
TOS: s.readCM.TOS,
HasTClass: s.readCM.HasTClass,
TClass: s.readCM.TClass,
HasIPPacketInfo: s.readCM.HasIPPacketInfo,
PacketInfo: s.readCM.PacketInfo,
HasOriginalDstAddress: s.readCM.HasOriginalDstAddress,
OriginalDstAddress: s.readCM.OriginalDstAddress,
IP: socket.IPControlMessages{
HasTimestamp: s.readCM.HasTimestamp && s.sockOptTimestamp,
Timestamp: s.readCM.Timestamp,
HasTOS: s.readCM.HasTOS,
TOS: s.readCM.TOS,
HasTClass: s.readCM.HasTClass,
TClass: s.readCM.TClass,
HasIPPacketInfo: s.readCM.HasIPPacketInfo,
PacketInfo: s.readCM.PacketInfo,
OriginalDstAddress: s.readCM.OriginalDstAddress,
},
}
}
+73 -1
View File
@@ -44,7 +44,79 @@ import (
// control messages.
type ControlMessages struct {
Unix transport.ControlMessages
IP tcpip.ControlMessages
IP IPControlMessages
}
// packetInfoToLinux converts IPPacketInfo from tcpip format to Linux format.
func packetInfoToLinux(packetInfo tcpip.IPPacketInfo) linux.ControlMessageIPPacketInfo {
var p linux.ControlMessageIPPacketInfo
p.NIC = int32(packetInfo.NIC)
copy(p.LocalAddr[:], []byte(packetInfo.LocalAddr))
copy(p.DestinationAddr[:], []byte(packetInfo.DestinationAddr))
return p
}
// NewIPControlMessages converts the tcpip ControlMessgaes (which does not
// have Linux specific format) to Linux format.
func NewIPControlMessages(family int, cmgs tcpip.ControlMessages) IPControlMessages {
var orgDstAddr linux.SockAddr
if cmgs.HasOriginalDstAddress {
orgDstAddr, _ = ConvertAddress(family, cmgs.OriginalDstAddress)
}
return IPControlMessages{
HasTimestamp: cmgs.HasTimestamp,
Timestamp: cmgs.Timestamp,
HasInq: cmgs.HasInq,
Inq: cmgs.Inq,
HasTOS: cmgs.HasTOS,
TOS: cmgs.TOS,
HasTClass: cmgs.HasTClass,
TClass: cmgs.TClass,
HasIPPacketInfo: cmgs.HasIPPacketInfo,
PacketInfo: packetInfoToLinux(cmgs.PacketInfo),
OriginalDstAddress: orgDstAddr,
}
}
// IPControlMessages contains socket control messages for IP sockets.
// This can contain Linux specific structures unlike tcpip.ControlMessages.
//
// +stateify savable
type IPControlMessages struct {
// HasTimestamp indicates whether Timestamp is valid/set.
HasTimestamp bool
// Timestamp is the time (in ns) that the last packet used to create
// the read data was received.
Timestamp int64
// HasInq indicates whether Inq is valid/set.
HasInq bool
// 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 uint8
// HasTClass indicates whether TClass is valid/set.
HasTClass bool
// TClass is the IPv6 traffic class of the associated packet.
TClass uint32
// HasIPPacketInfo indicates whether PacketInfo is set.
HasIPPacketInfo bool
// PacketInfo holds interface and address data on an incoming packet.
PacketInfo linux.ControlMessageIPPacketInfo
// OriginalDestinationAddress holds the original destination address
// and port of the incoming packet.
OriginalDstAddress linux.SockAddr
}
// Release releases Unix domain socket credentials and rights.
+1 -2
View File
@@ -784,9 +784,8 @@ func recvSingleMsg(t *kernel.Task, s socket.Socket, msgPtr usermem.Addr, flags i
}
defer cms.Release(t)
family, _, _ := s.Type()
controlData := make([]byte, 0, msg.ControlLen)
controlData = control.PackControlMessages(t, family, cms, controlData)
controlData = control.PackControlMessages(t, cms, controlData)
if cr, ok := s.(transport.Credentialer); ok && cr.Passcred() {
creds, _ := cms.Unix.Credentials.(control.SCMCredentials)
+1 -2
View File
@@ -787,9 +787,8 @@ func recvSingleMsg(t *kernel.Task, s socket.SocketVFS2, msgPtr usermem.Addr, fla
}
defer cms.Release(t)
family, _, _ := s.Type()
controlData := make([]byte, 0, msg.ControlLen)
controlData = control.PackControlMessages(t, family, cms, controlData)
controlData = control.PackControlMessages(t, cms, controlData)
if cr, ok := s.(transport.Credentialer); ok && cr.Passcred() {
creds, _ := cms.Unix.Credentials.(control.SCMCredentials)
+2 -2
View File
@@ -109,8 +109,8 @@ func (f *fakeTransportEndpoint) Write(p tcpip.Payloader, opts tcpip.WriteOptions
return int64(len(v)), nil, nil
}
func (*fakeTransportEndpoint) Peek([][]byte) (int64, tcpip.ControlMessages, *tcpip.Error) {
return 0, tcpip.ControlMessages{}, nil
func (*fakeTransportEndpoint) Peek([][]byte) (int64, *tcpip.Error) {
return 0, nil
}
// SetSockOpt sets a socket option. Currently not supported.
+1 -1
View File
@@ -554,7 +554,7 @@ type Endpoint interface {
// Peek reads data without consuming it from the endpoint.
//
// This method does not block if there is no data pending.
Peek([][]byte) (int64, ControlMessages, *Error)
Peek([][]byte) (int64, *Error)
// Connect connects the endpoint to its peer. Specifying a NIC is
// optional.
+2 -2
View File
@@ -332,8 +332,8 @@ func (e *endpoint) write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, <-c
}
// Peek only returns data from a single datagram, so do nothing here.
func (e *endpoint) Peek([][]byte) (int64, tcpip.ControlMessages, *tcpip.Error) {
return 0, tcpip.ControlMessages{}, nil
func (e *endpoint) Peek([][]byte) (int64, *tcpip.Error) {
return 0, nil
}
// SetSockOpt sets a socket option.
+2 -2
View File
@@ -206,8 +206,8 @@ func (*endpoint) Write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, <-cha
}
// Peek implements tcpip.Endpoint.Peek.
func (*endpoint) Peek([][]byte) (int64, tcpip.ControlMessages, *tcpip.Error) {
return 0, tcpip.ControlMessages{}, nil
func (*endpoint) Peek([][]byte) (int64, *tcpip.Error) {
return 0, nil
}
// Disconnect implements tcpip.Endpoint.Disconnect. Packet sockets cannot be
+2 -2
View File
@@ -386,8 +386,8 @@ func (e *endpoint) finishWrite(payloadBytes []byte, route *stack.Route) (int64,
}
// Peek implements tcpip.Endpoint.Peek.
func (e *endpoint) Peek([][]byte) (int64, tcpip.ControlMessages, *tcpip.Error) {
return 0, tcpip.ControlMessages{}, nil
func (e *endpoint) Peek([][]byte) (int64, *tcpip.Error) {
return 0, nil
}
// Disconnect implements tcpip.Endpoint.Disconnect.
+7 -7
View File
@@ -1498,7 +1498,7 @@ func (e *endpoint) Write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, <-c
// Peek reads data without consuming it from the endpoint.
//
// This method does not block if there is no data pending.
func (e *endpoint) Peek(vec [][]byte) (int64, tcpip.ControlMessages, *tcpip.Error) {
func (e *endpoint) Peek(vec [][]byte) (int64, *tcpip.Error) {
e.LockUser()
defer e.UnlockUser()
@@ -1506,10 +1506,10 @@ func (e *endpoint) Peek(vec [][]byte) (int64, tcpip.ControlMessages, *tcpip.Erro
// but has some pending unread data.
if s := e.EndpointState(); !s.connected() && s != StateClose {
if s == StateError {
return 0, tcpip.ControlMessages{}, e.hardErrorLocked()
return 0, e.hardErrorLocked()
}
e.stats.ReadErrors.InvalidEndpointState.Increment()
return 0, tcpip.ControlMessages{}, tcpip.ErrInvalidEndpointState
return 0, tcpip.ErrInvalidEndpointState
}
e.rcvListMu.Lock()
@@ -1518,9 +1518,9 @@ func (e *endpoint) Peek(vec [][]byte) (int64, tcpip.ControlMessages, *tcpip.Erro
if e.rcvBufUsed == 0 {
if e.rcvClosed || !e.EndpointState().connected() {
e.stats.ReadErrors.ReadClosed.Increment()
return 0, tcpip.ControlMessages{}, tcpip.ErrClosedForReceive
return 0, tcpip.ErrClosedForReceive
}
return 0, tcpip.ControlMessages{}, tcpip.ErrWouldBlock
return 0, tcpip.ErrWouldBlock
}
// Make a copy of vec so we can modify the slide headers.
@@ -1535,7 +1535,7 @@ func (e *endpoint) Peek(vec [][]byte) (int64, tcpip.ControlMessages, *tcpip.Erro
for len(v) > 0 {
if len(vec) == 0 {
return num, tcpip.ControlMessages{}, nil
return num, nil
}
if len(vec[0]) == 0 {
vec = vec[1:]
@@ -1550,7 +1550,7 @@ func (e *endpoint) Peek(vec [][]byte) (int64, tcpip.ControlMessages, *tcpip.Erro
}
}
return num, tcpip.ControlMessages{}, nil
return num, nil
}
// selectWindowLocked returns the new window without checking for shrinking or scaling
+2 -2
View File
@@ -4148,7 +4148,7 @@ func TestReadAfterClosedState(t *testing.T) {
// Check that peek works.
peekBuf := make([]byte, 10)
n, _, err := c.EP.Peek([][]byte{peekBuf})
n, err := c.EP.Peek([][]byte{peekBuf})
if err != nil {
t.Fatalf("Peek failed: %s", err)
}
@@ -4174,7 +4174,7 @@ func TestReadAfterClosedState(t *testing.T) {
t.Fatalf("got c.EP.Read(nil) = %s, want = %s", err, tcpip.ErrClosedForReceive)
}
if _, _, err := c.EP.Peek([][]byte{peekBuf}); err != tcpip.ErrClosedForReceive {
if _, err := c.EP.Peek([][]byte{peekBuf}); err != tcpip.ErrClosedForReceive {
t.Fatalf("got c.EP.Peek(...) = %s, want = %s", err, tcpip.ErrClosedForReceive)
}
}
+2 -2
View File
@@ -550,8 +550,8 @@ func (e *endpoint) write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, <-c
}
// Peek only returns data from a single datagram, so do nothing here.
func (e *endpoint) Peek([][]byte) (int64, tcpip.ControlMessages, *tcpip.Error) {
return 0, tcpip.ControlMessages{}, nil
func (e *endpoint) Peek([][]byte) (int64, *tcpip.Error) {
return 0, nil
}
// OnReuseAddressSet implements tcpip.SocketOptionsHandler.OnReuseAddressSet.