Internal change.

PiperOrigin-RevId: 294952610
This commit is contained in:
gVisor bot
2020-02-13 10:59:52 -08:00
parent 3ad6d30563
commit 69bf39e8a4
10 changed files with 278 additions and 7 deletions
+13
View File
@@ -411,6 +411,15 @@ type ControlMessageCredentials struct {
GID uint32
}
// A ControlMessageIPPacketInfo is IP_PKTINFO socket control message.
//
// ControlMessageIPPacketInfo represents struct in_pktinfo from linux/in.h.
type ControlMessageIPPacketInfo struct {
NIC int32
LocalAddr InetAddr
DestinationAddr InetAddr
}
// SizeOfControlMessageCredentials is the binary size of a
// ControlMessageCredentials struct.
var SizeOfControlMessageCredentials = int(binary.Size(ControlMessageCredentials{}))
@@ -431,6 +440,10 @@ const SizeOfControlMessageTOS = 1
// SizeOfControlMessageTClass is the size of an IPV6_TCLASS control message.
const SizeOfControlMessageTClass = 4
// SizeOfControlMessageIPPacketInfo is the size of an IP_PKTINFO
// control message.
const SizeOfControlMessageIPPacketInfo = 12
// SCM_MAX_FD is the maximum number of FDs accepted in a single sendmsg call.
// From net/scm.h.
const SCM_MAX_FD = 253
+1
View File
@@ -19,6 +19,7 @@ go_library(
"//pkg/sentry/socket",
"//pkg/sentry/socket/unix/transport",
"//pkg/syserror",
"//pkg/tcpip",
"//pkg/usermem",
],
)
+43
View File
@@ -26,6 +26,7 @@ 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"
)
@@ -338,6 +339,22 @@ func PackTClass(t *kernel.Task, tClass int32, 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))
return putCmsgStruct(
buf,
linux.SOL_IP,
linux.IP_PKTINFO,
t.Arch().Width(),
p,
)
}
// PackControlMessages packs control messages into the given buffer.
//
// We skip control messages specific to Unix domain sockets.
@@ -362,6 +379,10 @@ func PackControlMessages(t *kernel.Task, cmsgs socket.ControlMessages, buf []byt
buf = PackTClass(t, cmsgs.IP.TClass, buf)
}
if cmsgs.IP.HasIPPacketInfo {
buf = PackIPPacketInfo(t, cmsgs.IP.PacketInfo, buf)
}
return buf
}
@@ -394,6 +415,16 @@ func CmsgsSpace(t *kernel.Task, cmsgs socket.ControlMessages) int {
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 (
@@ -468,6 +499,18 @@ func Parse(t *kernel.Task, socketOrEndpoint interface{}, buf []byte) (socket.Con
binary.Unmarshal(buf[i:i+linux.SizeOfControlMessageTOS], usermem.ByteOrder, &cmsgs.IP.TOS)
i += binary.AlignUp(length, width)
case linux.IP_PKTINFO:
if length < linux.SizeOfControlMessageIPPacketInfo {
return socket.ControlMessages{}, syserror.EINVAL
}
cmsgs.IP.HasIPPacketInfo = true
var packetInfo linux.ControlMessageIPPacketInfo
binary.Unmarshal(buf[i:i+linux.SizeOfControlMessageIPPacketInfo], usermem.ByteOrder, &packetInfo)
cmsgs.IP.PacketInfo = NewIPPacketInfo(packetInfo)
i += binary.AlignUp(length, width)
default:
return socket.ControlMessages{}, syserror.EINVAL
}
+10 -1
View File
@@ -289,7 +289,7 @@ func (s *socketOperations) GetSockOpt(t *kernel.Task, level int, name int, outPt
switch level {
case linux.SOL_IP:
switch name {
case linux.IP_TOS, linux.IP_RECVTOS:
case linux.IP_TOS, linux.IP_RECVTOS, linux.IP_PKTINFO:
optlen = sizeofInt32
}
case linux.SOL_IPV6:
@@ -336,6 +336,8 @@ func (s *socketOperations) SetSockOpt(t *kernel.Task, level int, name int, opt [
switch name {
case linux.IP_TOS, linux.IP_RECVTOS:
optlen = sizeofInt32
case linux.IP_PKTINFO:
optlen = linux.SizeOfControlMessageIPPacketInfo
}
case linux.SOL_IPV6:
switch name {
@@ -473,7 +475,14 @@ func (s *socketOperations) RecvMsg(t *kernel.Task, dst usermem.IOSequence, flags
case syscall.IP_TOS:
controlMessages.IP.HasTOS = true
binary.Unmarshal(unixCmsg.Data[:linux.SizeOfControlMessageTOS], usermem.ByteOrder, &controlMessages.IP.TOS)
case syscall.IP_PKTINFO:
controlMessages.IP.HasIPPacketInfo = true
var packetInfo linux.ControlMessageIPPacketInfo
binary.Unmarshal(unixCmsg.Data[:linux.SizeOfControlMessageIPPacketInfo], usermem.ByteOrder, &packetInfo)
controlMessages.IP.PacketInfo = control.NewIPPacketInfo(packetInfo)
}
case syscall.SOL_IPV6:
switch unixCmsg.Header.Type {
case syscall.IPV6_TCLASS:
+32 -5
View File
@@ -1414,6 +1414,21 @@ func getSockOptIP(t *kernel.Task, ep commonEndpoint, name, outLen int, family in
}
return o, nil
case linux.IP_PKTINFO:
if outLen < sizeOfInt32 {
return nil, syserr.ErrInvalidArgument
}
v, err := ep.GetSockOptBool(tcpip.ReceiveIPPacketInfoOption)
if err != nil {
return nil, syserr.TranslateNetstackError(err)
}
var o int32
if v {
o = 1
}
return o, nil
default:
emitUnimplementedEventIP(t, name)
}
@@ -1762,6 +1777,7 @@ func setSockOptIPv6(t *kernel.Task, ep commonEndpoint, name int, optVal []byte)
linux.IPV6_IPSEC_POLICY,
linux.IPV6_JOIN_ANYCAST,
linux.IPV6_LEAVE_ANYCAST,
// TODO(b/148887420): Add support for IPV6_PKTINFO.
linux.IPV6_PKTINFO,
linux.IPV6_ROUTER_ALERT,
linux.IPV6_XFRM_POLICY,
@@ -1949,6 +1965,16 @@ func setSockOptIP(t *kernel.Task, ep commonEndpoint, name int, optVal []byte) *s
}
return syserr.TranslateNetstackError(ep.SetSockOptBool(tcpip.ReceiveTOSOption, v != 0))
case linux.IP_PKTINFO:
if len(optVal) == 0 {
return nil
}
v, err := parseIntOrChar(optVal)
if err != nil {
return err
}
return syserr.TranslateNetstackError(ep.SetSockOptBool(tcpip.ReceiveIPPacketInfoOption, v != 0))
case linux.IP_ADD_SOURCE_MEMBERSHIP,
linux.IP_BIND_ADDRESS_NO_PORT,
linux.IP_BLOCK_SOURCE,
@@ -1964,7 +1990,6 @@ func setSockOptIP(t *kernel.Task, ep commonEndpoint, name int, optVal []byte) *s
linux.IP_NODEFRAG,
linux.IP_OPTIONS,
linux.IP_PASSSEC,
linux.IP_PKTINFO,
linux.IP_RECVERR,
linux.IP_RECVFRAGSIZE,
linux.IP_RECVOPTS,
@@ -2395,10 +2420,12 @@ func (s *SocketOperations) nonBlockingRead(ctx context.Context, dst usermem.IOSe
func (s *SocketOperations) 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,
HasTimestamp: s.readCM.HasTimestamp && s.sockOptTimestamp,
Timestamp: s.readCM.Timestamp,
HasTOS: s.readCM.HasTOS,
TOS: s.readCM.TOS,
HasIPPacketInfo: s.readCM.HasIPPacketInfo,
PacketInfo: s.readCM.PacketInfo,
},
}
}
+25
View File
@@ -328,6 +328,12 @@ type ControlMessages struct {
// Tclass is the IPv6 traffic class of the associated packet.
TClass int32
// HasIPPacketInfo indicates whether PacketInfo is set.
HasIPPacketInfo bool
// PacketInfo holds interface and address data on an incoming packet.
PacketInfo IPPacketInfo
}
// Endpoint is the interface implemented by transport protocols (e.g., tcp, udp)
@@ -503,6 +509,11 @@ const (
// V6OnlyOption is used by {G,S}etSockOptBool to specify whether an IPv6
// socket is to be restricted to sending and receiving IPv6 packets only.
V6OnlyOption
// ReceiveIPPacketInfoOption is used by {G,S}etSockOptBool to specify
// if more inforamtion is provided with incoming packets such
// as interface index and address.
ReceiveIPPacketInfoOption
)
// SockOptInt represents socket options which values have the int type.
@@ -685,6 +696,20 @@ type IPv4TOSOption uint8
// for all subsequent outgoing IPv6 packets from the endpoint.
type IPv6TrafficClassOption uint8
// IPPacketInfo is the message struture for IP_PKTINFO.
//
// +stateify savable
type IPPacketInfo struct {
// NIC is the ID of the NIC to be used.
NIC NICID
// LocalAddr is the local address.
LocalAddr Address
// DestinationAddr is the destination address.
DestinationAddr Address
}
// Route is a row in the routing table. It specifies through which NIC (and
// gateway) sets of packets should be routed. A row is considered viable if the
// masked target address matches the destination address in the row.
+26
View File
@@ -29,6 +29,7 @@ import (
type udpPacket struct {
udpPacketEntry
senderAddress tcpip.FullAddress
packetInfo tcpip.IPPacketInfo
data buffer.VectorisedView `state:".(buffer.VectorisedView)"`
timestamp int64
tos uint8
@@ -118,6 +119,9 @@ type endpoint struct {
// as ancillary data to ControlMessages on Read.
receiveTOS bool
// receiveIPPacketInfo determines if the packet info is returned by Read.
receiveIPPacketInfo bool
// shutdownFlags represent the current shutdown state of the endpoint.
shutdownFlags tcpip.ShutdownFlags
@@ -254,11 +258,17 @@ func (e *endpoint) Read(addr *tcpip.FullAddress) (buffer.View, tcpip.ControlMess
}
e.mu.RLock()
receiveTOS := e.receiveTOS
receiveIPPacketInfo := e.receiveIPPacketInfo
e.mu.RUnlock()
if receiveTOS {
cm.HasTOS = true
cm.TOS = p.tos
}
if receiveIPPacketInfo {
cm.HasIPPacketInfo = true
cm.PacketInfo = p.packetInfo
}
return p.data.ToView(), cm, nil
}
@@ -495,6 +505,13 @@ func (e *endpoint) SetSockOptBool(opt tcpip.SockOptBool, v bool) *tcpip.Error {
}
e.v6only = v
return nil
case tcpip.ReceiveIPPacketInfoOption:
e.mu.Lock()
e.receiveIPPacketInfo = v
e.mu.Unlock()
return nil
}
return nil
@@ -703,6 +720,12 @@ func (e *endpoint) GetSockOptBool(opt tcpip.SockOptBool) (bool, *tcpip.Error) {
e.mu.RUnlock()
return v, nil
case tcpip.ReceiveIPPacketInfoOption:
e.mu.RLock()
v := e.receiveIPPacketInfo
e.mu.RUnlock()
return v, nil
}
return false, tcpip.ErrUnknownProtocolOption
@@ -1247,6 +1270,9 @@ func (e *endpoint) HandlePacket(r *stack.Route, id stack.TransportEndpointID, pk
switch r.NetProto {
case header.IPv4ProtocolNumber:
packet.tos, _ = header.IPv4(pkt.NetworkHeader).TOS()
packet.packetInfo.LocalAddr = r.LocalAddress
packet.packetInfo.DestinationAddr = r.RemoteAddress
packet.packetInfo.NIC = r.NICID()
}
packet.timestamp = e.stack.NowNanoseconds()
@@ -357,5 +357,49 @@ TEST_P(UDPSocketPairTest, SetReuseAddrReusePort) {
EXPECT_EQ(get, kSockOptOn);
}
// Test getsockopt for a socket which is not set with IP_PKTINFO option.
TEST_P(UDPSocketPairTest, IPPKTINFODefault) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
int get = -1;
socklen_t get_len = sizeof(get);
ASSERT_THAT(
getsockopt(sockets->first_fd(), SOL_IP, IP_PKTINFO, &get, &get_len),
SyscallSucceedsWithValue(0));
EXPECT_EQ(get_len, sizeof(get));
EXPECT_EQ(get, kSockOptOff);
}
// Test setsockopt and getsockopt for a socket with IP_PKTINFO option.
TEST_P(UDPSocketPairTest, SetAndGetIPPKTINFO) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
int level = SOL_IP;
int type = IP_PKTINFO;
// Check getsockopt before IP_PKTINFO is set.
int get = -1;
socklen_t get_len = sizeof(get);
ASSERT_THAT(setsockopt(sockets->first_fd(), level, type, &kSockOptOn,
sizeof(kSockOptOn)),
SyscallSucceedsWithValue(0));
ASSERT_THAT(getsockopt(sockets->first_fd(), level, type, &get, &get_len),
SyscallSucceedsWithValue(0));
EXPECT_EQ(get, kSockOptOn);
EXPECT_EQ(get_len, sizeof(get));
ASSERT_THAT(setsockopt(sockets->first_fd(), level, type, &kSockOptOff,
sizeof(kSockOptOff)),
SyscallSucceedsWithValue(0));
ASSERT_THAT(getsockopt(sockets->first_fd(), level, type, &get, &get_len),
SyscallSucceedsWithValue(0));
EXPECT_EQ(get, kSockOptOff);
EXPECT_EQ(get_len, sizeof(get));
}
} // namespace testing
} // namespace gvisor
@@ -15,6 +15,7 @@
#include "test/syscalls/linux/socket_ipv4_udp_unbound.h"
#include <arpa/inet.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/un.h>
@@ -2128,5 +2129,88 @@ TEST_P(IPv4UDPUnboundSocketTest, ReuseAddrReusePortDistribution) {
SyscallSucceedsWithValue(kMessageSize));
}
// Test that socket will receive packet info control message.
TEST_P(IPv4UDPUnboundSocketTest, SetAndReceiveIPPKTINFO) {
// TODO(gvisor.dev/issue/1202): ioctl() is not supported by hostinet.
SKIP_IF((IsRunningWithHostinet()));
auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
auto receiver = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
auto sender_addr = V4Loopback();
int level = SOL_IP;
int type = IP_PKTINFO;
ASSERT_THAT(
bind(receiver->get(), reinterpret_cast<sockaddr*>(&sender_addr.addr),
sender_addr.addr_len),
SyscallSucceeds());
socklen_t sender_addr_len = sender_addr.addr_len;
ASSERT_THAT(getsockname(receiver->get(),
reinterpret_cast<sockaddr*>(&sender_addr.addr),
&sender_addr_len),
SyscallSucceeds());
EXPECT_EQ(sender_addr_len, sender_addr.addr_len);
auto receiver_addr = V4Loopback();
reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port =
reinterpret_cast<sockaddr_in*>(&sender_addr.addr)->sin_port;
ASSERT_THAT(
connect(sender->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr),
receiver_addr.addr_len),
SyscallSucceeds());
// Allow socket to receive control message.
ASSERT_THAT(
setsockopt(receiver->get(), level, type, &kSockOptOn, sizeof(kSockOptOn)),
SyscallSucceeds());
// Prepare message to send.
constexpr size_t kDataLength = 1024;
msghdr sent_msg = {};
iovec sent_iov = {};
char sent_data[kDataLength];
sent_iov.iov_base = sent_data;
sent_iov.iov_len = kDataLength;
sent_msg.msg_iov = &sent_iov;
sent_msg.msg_iovlen = 1;
sent_msg.msg_flags = 0;
ASSERT_THAT(RetryEINTR(sendmsg)(sender->get(), &sent_msg, 0),
SyscallSucceedsWithValue(kDataLength));
msghdr received_msg = {};
iovec received_iov = {};
char received_data[kDataLength];
char received_cmsg_buf[CMSG_SPACE(sizeof(in_pktinfo))] = {};
size_t cmsg_data_len = sizeof(in_pktinfo);
received_iov.iov_base = received_data;
received_iov.iov_len = kDataLength;
received_msg.msg_iov = &received_iov;
received_msg.msg_iovlen = 1;
received_msg.msg_controllen = CMSG_LEN(cmsg_data_len);
received_msg.msg_control = received_cmsg_buf;
ASSERT_THAT(RetryEINTR(recvmsg)(receiver->get(), &received_msg, 0),
SyscallSucceedsWithValue(kDataLength));
cmsghdr* cmsg = CMSG_FIRSTHDR(&received_msg);
ASSERT_NE(cmsg, nullptr);
EXPECT_EQ(cmsg->cmsg_len, CMSG_LEN(cmsg_data_len));
EXPECT_EQ(cmsg->cmsg_level, level);
EXPECT_EQ(cmsg->cmsg_type, type);
// Get loopback index.
ifreq ifr = {};
absl::SNPrintF(ifr.ifr_name, IFNAMSIZ, "lo");
ASSERT_THAT(ioctl(sender->get(), SIOCGIFINDEX, &ifr), SyscallSucceeds());
ASSERT_NE(ifr.ifr_ifindex, 0);
// Check the data
in_pktinfo received_pktinfo = {};
memcpy(&received_pktinfo, CMSG_DATA(cmsg), sizeof(in_pktinfo));
EXPECT_EQ(received_pktinfo.ipi_ifindex, ifr.ifr_ifindex);
EXPECT_EQ(received_pktinfo.ipi_spec_dst.s_addr, htonl(INADDR_LOOPBACK));
EXPECT_EQ(received_pktinfo.ipi_addr.s_addr, htonl(INADDR_LOOPBACK));
}
} // namespace testing
} // namespace gvisor
@@ -1495,6 +1495,5 @@ TEST_P(UdpSocketTest, SendAndReceiveTOS) {
memcpy(&received_tos, CMSG_DATA(cmsg), sizeof(received_tos));
EXPECT_EQ(received_tos, sent_tos);
}
} // namespace testing
} // namespace gvisor